智能字幕校准系统实战(一):微服务架构设计与技术栈选型

系列文章:《智能字幕校准系统实战:从架构到算法的全栈技术解析》
本文为第1篇:微服务架构设计与技术栈选型
阅读时间:15分钟
难度:★★★(中级)
标签微服务架构 Spring Boot Python 系统设计 技术选型


前情回顾

系列开篇中,我介绍了这个智能字幕校准系统的业务背景:

业务痛点

  • 人工校准字幕耗时长(10分钟视频需30-60分钟)
  • 人力成本高($15-25/小时)
  • 难以规模化(5种语言需要5个熟练工人)

我们的目标

  • 自动化率90%+
  • 时间节省80%
  • 成本降低70%
  • 支持5种语言(英、西、葡、日、法)

今天这篇文章,我将详细讲解如何设计这个系统的架构,以及为什么做出这些技术选型决策


架构设计的核心问题

在开始编码之前,我们需要回答几个关键问题:

问题1:单体应用 vs 微服务?

单体应用的诱惑

  • 开发简单,一个项目搞定
  • 部署方便,一个war包解决
  • 调试容易,本地就能运行

但我们选择了微服务,为什么?

让我们看看这个系统的特点:

核心功能模块:
1. 业务逻辑层(Java擅长)
   - 用户认证、权限管理
   - 任务流程编排
   - 文件管理
   - API接口

2. AI算法层(Python擅长)
   - NLP文本处理
   - 智能匹配算法
   - 数据分析

3. 翻译爬取层(特殊环境)
   - Selenium WebDriver
   - 需要Chrome浏览器
   - 高内存消耗

4. 外部服务集成层
   - Azure STT API
   - Sonix API
   - DeepL API

问题显而易见

  1. 技术栈异构:Java和Python各有所长,强行统一会降低效率
  2. 部署环境不同:Selenium需要浏览器环境,AI服务需要大内存和GPU
  3. 扩展需求不同:AI计算密集,需要独立扩展;业务逻辑IO密集,扩展需求不同
  4. 团队技能不同:后端工程师写Java,算法工程师写Python

结论:微服务是更合适的选择。


问题2:如何拆分微服务?

微服务拆分有很多原则,常见的有:

  • 按业务领域拆分(DDD)
  • 按技术栈拆分
  • 按团队拆分
  • 按数据边界拆分

我们采用的是混合策略

服务 拆分依据 理由
chronos-web-after 业务领域 核心业务逻辑,是系统的大脑
chronos-python 技术栈 Python擅长AI算法
chronos-translate 技术栈+部署环境 Selenium需要独立环境
chronos-web-front 技术栈 前后端分离

拆分原则

  1. 高内聚:每个服务职责单一明确
  2. 低耦合:服务间通过定义良好的接口通信
  3. 独立部署:各服务可以独立发布、扩展
  4. 技术自由:每个服务可以选择最适合的技术栈

问题3:微服务如何通信?

常见方案对比

方案 优点 缺点 是否采用
Spring Cloud全家桶 生态完善,功能强大 复杂度高,学习成本大 [NO] 杀鸡用牛刀
gRPC 性能高,类型安全 需要proto定义,调试不便 [NO] 过度设计
HTTP REST 简单直接,易于调试 性能一般 [OK] 适合中小规模
消息队列(MQ) 解耦彻底,削峰填谷 增加运维复杂度 [NO] 当前规模不需要
Redis队列 轻量级,易于运维 功能简单 [OK] 满足需求

我们的选择

  • 同步调用:HTTP REST(用于实时响应,如用户登录)
  • 异步调用:Redis任务队列(用于耗时任务,如字幕校准)

为什么不用Spring Cloud?

  • 我们的服务数量只有4个,不需要复杂的服务治理
  • 部署规模小(单机或2-3台服务器),不需要分布式追踪
  • 团队规模小,维护成本要可控

简单就是美!


系统架构设计

整体架构图

┌─────────────────────────────────────────────────────────────┐
│                        用户层                                 │
│                                                              │
│    ┌──────────────────────────────────────────────┐         │
│    │   chronos-web-front (Vue.js 2.6)            │         │
│    │   - Element UI 组件库                        │         │
│    │   - Vuex 状态管理                            │         │
│    │   - Axios HTTP客户端                         │         │
│    │   - WebSocket 实时推送                       │         │
│    └──────────────────────────────────────────────┘         │
│                          │ HTTP/WS                          │
└──────────────────────────┼──────────────────────────────────┘
                           │
┌──────────────────────────┼──────────────────────────────────┐
│                    核心业务层                                 │
│                          │                                   │
│    ┌──────────────────────────────────────────────┐         │
│    │   chronos-web-after (Spring Boot 2.7.9)     │         │
│    │                                              │         │
│    │   Controller层:                             │         │
│    │   ├─ CalibrationController - 20+ REST API  │         │
│    │   ├─ TranscriptionController               │         │
│    │   ├─ TranslationController                 │         │
│    │   └─ UserController                        │         │
│    │                                              │         │
│    │   Service层:                                │         │
│    │   ├─ CalibrationService (@Scheduled)       │         │
│    │   ├─ TaskQueue (Redis操作)                 │         │
│    │   ├─ AwsS3Service (文件管理)               │         │
│    │   └─ UserService (认证授权)                │         │
│    │                                              │         │
│    │   Task层:                                   │         │
│    │   ├─ CalibrationTask (任务执行)            │         │
│    │   ├─ TranscriptionTask                     │         │
│    │   └─ TranslationTask                       │         │
│    └──────────────────────────────────────────────┘         │
│                │              │              │               │
│         HTTP   │       Redis  │       HTTP   │               │
│                │       Queue  │              │               │
└────────────────┼──────────────┼──────────────┼───────────────┘
                 │              │              │
         ┌───────┴─────┐   ┌────┴─────┐   ┌───┴──────────┐
         │             │   │          │   │              │
┌────────┴─────┐ ┌─────┴───┴────┐ ┌──┴───┴──────┐ ┌─────┴─────┐
│  AI算法服务   │ │  翻译爬取服务  │ │  外部STT服务 │ │  外部翻译  │
│              │ │              │ │             │ │   服务    │
│ chronos-     │ │ chronos-     │ │  - Azure    │ │ - DeepL   │
│ python       │ │ translate    │ │    Speech   │ │ - AWS     │
│              │ │              │ │  - Sonix    │ │   Trans   │
│ - Spacy NLP  │ │ - Selenium   │ │             │ │           │
│ - 6级算法    │ │   WebDriver  │ │             │ │           │
│ - 质量评估   │ │ - Chrome     │ │             │ │           │
│              │ │   Headless   │ │             │ │           │
└──────────────┘ └──────────────┘ └─────────────┘ └───────────┘
         │              │
         └──────┬───────┘
                │
┌───────────────┼──────────────────────────────────────────────┐
│           存储层                                              │
│                                                              │
│   ┌──────────┐    ┌───────────┐    ┌──────────────┐        │
│   │  MySQL   │    │   Redis   │    │   AWS S3     │        │
│   │          │    │           │    │              │        │
│   │ - 任务表  │    │ - 任务队列 │    │ - 音频文件    │        │
│   │ - 用户表  │    │ - Session │    │ - 字幕文件    │        │
│   │ - 字幕表  │    │ - 缓存    │    │ - 结果文件    │        │
│   └──────────┘    └───────────┘    └──────────────┘        │
└──────────────────────────────────────────────────────────────┘

架构特点

  1. 分层清晰:用户层、业务层、服务层、存储层
  2. 职责分离:Java做业务编排,Python做AI计算,Selenium做爬取
  3. 异步解耦:Redis队列实现任务异步处理
  4. 云存储:S3解决大文件存储和传输问题

四大微服务详解

1. chronos-web-after:核心业务服务

角色定位:系统的大脑,负责业务流程编排和任务调度

技术栈

<dependencies>
    <!-- Spring Boot 核心 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.7.9</version>
    </dependency>

    <!-- Spring Security + JWT 认证 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <!-- Spring Data JPA 数据访问 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <!-- Redis 队列和缓存 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    <!-- AWS SDK (S3存储) -->
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>s3</artifactId>
        <version>2.20.26</version>
    </dependency>
</dependencies>

核心功能模块

1.1 用户认证与权限管理

@RestController
@RequestMapping("/api/auth")
public class AuthController {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtTokenProvider tokenProvider;

    /**
     * 用户登录
     */
    @PostMapping("/login")
    public Result login(@RequestBody LoginRequest request) {
        // 验证用户名密码
        Authentication authentication = authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(
                request.getUsername(),
                request.getPassword()
            )
        );

        SecurityContextHolder.getContext().setAuthentication(authentication);

        // 生成JWT Token
        String token = tokenProvider.generateToken(authentication);

        return Result.success(new AuthResponse(token));
    }
}

1.2 任务流程编排

@Service
public class CalibrationService {

    @Autowired
    private TaskRepository taskRepository;

    @Autowired
    private TaskQueue taskQueue;

    @Autowired
    private AwsS3Service s3Service;

    /**
     * 创建校准任务
     */
    @Transactional
    public Task createCalibrationTask(CalibrationRequest request) {
        // 1. 创建任务记录
        Task task = new Task();
        task.setUserId(getCurrentUserId());
        task.setTaskType(TaskType.CALIBRATION);
        task.setStatus(TaskStatus.PENDING);
        task.setLanguage(request.getLanguage());
        task.setCreatedTime(LocalDateTime.now());

        taskRepository.save(task);

        // 2. 生成S3预签名上传URL
        String refSrtUploadUrl = s3Service.generateUploadUrl(
            "subtitles/" + task.getId() + "/ref.srt",
            Duration.ofMinutes(30)
        );

        String audioUploadUrl = s3Service.generateUploadUrl(
            "audio/" + task.getId() + "/audio.mp3",
            Duration.ofMinutes(30)
        );

        task.setRefSrtUploadUrl(refSrtUploadUrl);
        task.setAudioUploadUrl(audioUploadUrl);

        taskRepository.save(task);

        return task;
    }

    /**
     * 开始执行任务
     */
    public void startTask(String taskId) {
        // 1. 验证文件已上传
        Task task = taskRepository.findById(taskId)
            .orElseThrow(() -> new TaskNotFoundException(taskId));

        if (!s3Service.exists(task.getRefSrtUrl()) ||
            !s3Service.exists(task.getAudioUrl())) {
            throw new FilesNotUploadedException();
        }

        // 2. 加入Redis任务队列
        taskQueue.push(taskId);

        // 3. 更新状态
        task.setStatus(TaskStatus.PENDING);
        taskRepository.save(task);
    }

    /**
     * 定时任务调度器:每5秒扫描一次队列
     */
    @Scheduled(fixedDelay = 5000)
    public void scheduleTasks() {
        // 从队列中取出任务
        String taskId = taskQueue.poll(1, TimeUnit.SECONDS);

        if (taskId != null) {
            // 提交到线程池执行
            calibrationExecutor.submit(() -> {
                try {
                    processCalibrationTask(taskId);
                } catch (Exception e) {
                    handleTaskFailure(taskId, e);
                }
            });
        }
    }
}

1.3 状态机管理

@Service
public class TaskStateMachine {

    /**
     * 状态转换规则
     */
    private static final Map<TaskStatus, List<TaskStatus>> ALLOWED_TRANSITIONS = Map.of(
        PENDING, List.of(DOWNLOADING, FAILED),
        DOWNLOADING, List.of(DOWNLOADED, FAILED),
        DOWNLOADED, List.of(TRANSCRIBING, FAILED),
        TRANSCRIBING, List.of(TRANSCRIBED, FAILED),
        TRANSCRIBED, List.of(CALIBRATING, FAILED),
        CALIBRATING, List.of(CALIBRATED, FAILED),
        CALIBRATED, List.of(UPLOADING, FAILED),
        UPLOADING, List.of(COMPLETED, FAILED)
    );

    /**
     * 状态转换
     */
    @Transactional
    public void transition(String taskId, TaskStatus newStatus, String message) {
        Task task = taskRepository.findById(taskId).orElseThrow();

        // 验证转换合法性
        if (!canTransition(task.getStatus(), newStatus)) {
            throw new IllegalStateTransitionException(
                String.format("Cannot transition from %s to %s",
                    task.getStatus(), newStatus)
            );
        }

        // 更新状态
        task.setStatus(newStatus);
        task.setProgress(newStatus.getProgress());
        task.setMessage(message);
        task.setUpdatedTime(LocalDateTime.now());

        taskRepository.save(task);

        // WebSocket推送给前端
        webSocketService.sendTaskUpdate(taskId, task);

        log.info("Task {} transitioned to {}: {}", taskId, newStatus, message);
    }

    private boolean canTransition(TaskStatus from, TaskStatus to) {
        List<TaskStatus> allowed = ALLOWED_TRANSITIONS.get(from);
        return allowed != null && allowed.contains(to);
    }
}

设计亮点

  1. [OK] 状态机保证流程一致性:防止非法状态转换
  2. [OK] 定时调度器自动处理任务:用户无需等待
  3. [OK] ThreadPoolExecutor控制并发:避免系统过载
  4. [OK] WebSocket实时推送进度:良好的用户体验

2. chronos-python:AI校准服务

角色定位:系统的智慧核心,负责NLP处理和智能匹配算法

技术栈

# requirements.txt
spacy==3.5.0
numpy==1.24.2
pandas==1.5.3
scikit-learn==1.2.2

# 语言模型
en_core_web_md==3.5.0
es_core_news_md==3.5.0
pt_core_news_md==3.5.0
ja_core_news_md==3.5.0
fr_core_news_md==3.5.0

# 日语分词
mecab-python3==1.0.5

核心模块

2.1 服务入口

# chronos.py
class ChronosService:
    """校准服务主类"""

    def __init__(self):
        self.train_set = {}  # 存储已加载的语言模型

    def load_train_set_by_lang(self, lang):
        """
        按需加载Spacy语言模型
        避免启动时加载所有模型(耗时+内存)
        """
        if lang not in self.train_set:
            model_map = {
                'en': 'en_core_web_md',
                'es': 'es_core_news_md',
                'pt': 'pt_core_news_md',
                'ja': 'ja_core_news_md',
                'fr': 'fr_core_news_md'
            }

            model_name = model_map.get(lang, 'en_core_web_md')
            print(f"Loading Spacy model: {model_name}...")

            self.train_set[lang] = spacy.load(model_name)
            print(f"Model loaded successfully.")

        return self.train_set[lang]

    def start_calibration(self, task_id, ref_srt_path, stt_json_path, lang):
        """
        开始校准流程

        Args:
            task_id: 任务ID
            ref_srt_path: 参考字幕路径
            stt_json_path: STT结果路径
            lang: 语言代码

        Returns:
            校准后的SRT文件路径
        """
        # 1. 加载语言模型
        nlp = self.load_train_set_by_lang(lang)

        # 2. 解析参考字幕
        sub_segs = self.parse_subtitle(ref_srt_path, lang, nlp)

        # 3. 解析STT词级时间戳
        to_match_words = self.parse_stt_result(stt_json_path)

        # 4. 执行6级匹配算法(核心)
        sync = DirectSync()
        result = sync.subtitle_time_sync(sub_segs, to_match_words, nlp)

        # 5. 生成校准后的SRT
        output_path = f"/tmp/calibration/{task_id}/result.srt"
        self.write_srt(result, output_path)

        return output_path

    def start_preprocessing_list(self, task_id, ref_srt_path, stt_json_list, lang):
        """
        批量预处理:处理多个STT结果,选取最佳匹配

        适用场景:
        - 同一音频使用不同STT服务(Azure vs Sonix)
        - 需要选择质量最好的结果
        """
        nlp = self.load_train_set_by_lang(lang)
        sub_segs = self.parse_subtitle(ref_srt_path, lang, nlp)

        best_result = None
        best_score = 0

        for stt_json_path in stt_json_list:
            to_match_words = self.parse_stt_result(stt_json_path)

            # 执行校准
            sync = DirectSync()
            result = sync.subtitle_time_sync(sub_segs, to_match_words, nlp)

            # 计算质量分数
            score = self.calculate_quality_score(result)

            if score > best_score:
                best_score = score
                best_result = result

        # 返回最佳结果
        output_path = f"/tmp/calibration/{task_id}/result_best.srt"
        self.write_srt(best_result, output_path)

        return output_path

    def calculate_quality_score(self, result):
        """
        计算校准质量分数

        综合评分 = 锚点覆盖率 × 0.6 + (1 - 时间交叉率) × 0.4
        """
        anchor_coverage = result.get_anchor_coverage()  # 匹配成功的字幕占比
        time_crossing_rate = result.get_time_crossing_rate()  # 时间交叉占比

        return anchor_coverage * 0.6 + (1 - time_crossing_rate) * 0.4

设计亮点

  1. [OK] 按需加载语言模型:节省内存和启动时间
  2. [OK] 批量处理+质量评分:自动选择最佳结果
  3. [OK] 质量指标量化:可追踪优化效果

3. chronos-translate:翻译爬取服务

角色定位:通过Selenium爬取Google翻译,作为免费翻译备选方案

技术栈

<dependencies>
    <!-- Spring Boot -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.5.5</version>
    </dependency>

    <!-- Selenium WebDriver -->
    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-java</artifactId>
        <version>4.7.2</version>
    </dependency>
</dependencies>

核心实现

@Service
public class GoogleTranslateService {

    private WebDriver driver;

    @PostConstruct
    public void initDriver() {
        ChromeOptions options = new ChromeOptions();
        options.addArguments("--headless");  // 无头模式
        options.addArguments("--no-sandbox");
        options.addArguments("--disable-dev-shm-usage");
        options.addArguments("--disable-gpu");

        driver = new ChromeDriver(options);
    }

    /**
     * 翻译文本
     */
    public String translate(String text, String sourceLang, String targetLang) {
        // 1. 智能分段(Google限制5000字符)
        List<String> segments = splitText(text, 5000);

        List<String> translatedSegments = new ArrayList<>();

        for (String segment : segments) {
            // 2. 构造URL
            String url = String.format(
                "https://translate.google.com/?sl=%s&tl=%s&text=%s",
                sourceLang,
                targetLang,
                URLEncoder.encode(segment, StandardCharsets.UTF_8)
            );

            driver.get(url);

            // 3. 等待翻译结果
            WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30));
            WebElement resultElement = wait.until(
                ExpectedConditions.presenceOfElementLocated(
                    By.cssSelector("span[data-language-for-alternatives='" + targetLang + "']")
                )
            );

            String translated = resultElement.getText();
            translatedSegments.add(translated);

            // 4. 随机延迟(反爬虫)
            Thread.sleep(1000 + new Random().nextInt(2000));
        }

        return String.join("", translatedSegments);
    }

    /**
     * 智能分段:在标点符号处分割
     */
    private List<String> splitText(String text, int maxLength) {
        List<String> segments = new ArrayList<>();
        int start = 0;

        while (start < text.length()) {
            int end = Math.min(start + maxLength, text.length());

            // 尽量在句号处分割
            if (end < text.length()) {
                int lastPeriod = text.lastIndexOf('.', end);
                if (lastPeriod > start) {
                    end = lastPeriod + 1;
                }
            }

            segments.add(text.substring(start, end));
            start = end;
        }

        return segments;
    }

    @PreDestroy
    public void cleanup() {
        if (driver != null) {
            driver.quit();
        }
    }
}

设计亮点

  1. [OK] Headless模式:无需图形界面,节省资源
  2. [OK] 智能分段:处理长文本,在句号处分割保证语义完整
  3. [OK] 反爬虫策略:随机延迟,避免被封IP

为什么需要这个服务?

  • DeepL免费额度:500K字符/月
  • AWS Translate:按量付费
  • Google翻译爬取:免费备选方案,降低成本

4. chronos-web-front:前端UI服务

角色定位:用户交互界面

技术栈

{
  "dependencies": {
    "vue": "^2.6.11",
    "element-ui": "^2.15.6",
    "vuex": "^3.6.2",
    "vue-router": "^3.5.1",
    "axios": "^0.26.1"
  }
}

核心功能

// 任务创建流程
async createCalibrationTask() {
  try {
    // 1. 创建任务,获取上传URL
    const { data } = await axios.post('/api/calibration/create', {
      language: this.selectedLanguage
    });

    const { taskId, refSrtUploadUrl, audioUploadUrl } = data;

    // 2. 直接上传文件到S3(不经过服务器)
    await this.uploadToS3(refSrtUploadUrl, this.refSrtFile);
    await this.uploadToS3(audioUploadUrl, this.audioFile);

    // 3. 通知服务器开始处理
    await axios.post(`/api/calibration/${taskId}/start`);

    // 4. 建立WebSocket连接,监听进度
    this.connectWebSocket(taskId);

    this.$message.success('任务创建成功!');
  } catch (error) {
    this.$message.error('任务创建失败:' + error.message);
  }
},

uploadToS3(presignedUrl, file) {
  return axios.put(presignedUrl, file, {
    headers: {
      'Content-Type': 'application/octet-stream'
    },
    onUploadProgress: (progressEvent) => {
      const percentCompleted = Math.round(
        (progressEvent.loaded * 100) / progressEvent.total
      );
      this.uploadProgress = percentCompleted;
    }
  });
},

connectWebSocket(taskId) {
  const ws = new WebSocket(`ws://localhost:8080/ws/task/${taskId}`);

  ws.onmessage = (event) => {
    const update = JSON.parse(event.data);

    // 更新UI
    this.taskStatus = update.status;
    this.taskProgress = update.progress;
    this.taskMessage = update.message;

    // 任务完成
    if (update.status === 'COMPLETED') {
      this.$message.success('任务完成!');
      this.downloadUrl = update.downloadUrl;
      ws.close();
    }

    // 任务失败
    if (update.status === 'FAILED') {
      this.$message.error('任务失败:' + update.message);
      ws.close();
    }
  };
}

服务间通信设计

Redis任务队列

为什么选择Redis而非RabbitMQ?

维度 Redis RabbitMQ 我们的选择
轻量级 [OK] 内存占用小 [NO] 较重 [OK]
易于运维 [OK] 配置简单 [NO] 配置复杂 [OK]
学习成本 [OK] 低 [NO] 高 [OK]
消息确认 [WARN] 需手动实现 [OK] 内置ACK 可接受
任务规模 [WARN] <1000 QPS [OK] >10000 QPS [OK] 我们<100 QPS
持久化 [OK] AOF [OK] [OK]

结论:对于中小规模任务(<1000 QPS),Redis是更简单、更合适的选择。

队列设计

@Component
public class TaskQueue {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    private static final String PENDING_QUEUE = "calibration:pending";
    private static final String PROCESSING_HASH = "calibration:processing";

    /**
     * 加入队列
     */
    public void push(String taskId) {
        redisTemplate.opsForList().rightPush(PENDING_QUEUE, taskId);
        log.info("Task {} pushed to queue", taskId);
    }

    /**
     * 取出任务(阻塞式)
     */
    public String poll(long timeout, TimeUnit unit) {
        return redisTemplate.opsForList().leftPop(PENDING_QUEUE, timeout, unit);
    }

    /**
     * 标记为处理中
     */
    public void markProcessing(String taskId, String workerId) {
        redisTemplate.opsForHash().put(
            PROCESSING_HASH,
            taskId,
            workerId + ":" + System.currentTimeMillis()
        );
    }

    /**
     * 标记为完成
     */
    public void markCompleted(String taskId) {
        redisTemplate.opsForHash().delete(PROCESSING_HASH, taskId);
    }
}

数据库设计

核心表结构

1. tbl_task(任务表)

CREATE TABLE tbl_task (
    id VARCHAR(36) PRIMARY KEY COMMENT '任务ID(UUID)',
    user_id VARCHAR(36) NOT NULL COMMENT '用户ID',
    task_type VARCHAR(20) NOT NULL COMMENT '任务类型:CALIBRATION/TRANSCRIPTION/TRANSLATION',
    status VARCHAR(20) NOT NULL COMMENT '状态:PENDING/DOWNLOADING/.../COMPLETED/FAILED',
    progress INT DEFAULT 0 COMMENT '进度:0-100',
    message TEXT COMMENT '状态消息',

    -- 文件路径
    ref_srt_url VARCHAR(500) COMMENT '参考字幕S3路径',
    audio_url VARCHAR(500) COMMENT '音频文件S3路径',
    result_url VARCHAR(500) COMMENT '结果文件S3路径',

    -- 任务配置
    language VARCHAR(10) COMMENT '语言:en/es/pt/ja/fr',
    source_lang VARCHAR(10) COMMENT '源语言',
    target_lang VARCHAR(10) COMMENT '目标语言',

    -- 质量指标
    anchor_coverage DECIMAL(5,2) COMMENT '锚点覆盖率',
    time_crossing_rate DECIMAL(5,2) COMMENT '时间交叉率',
    quality_score DECIMAL(5,2) COMMENT '质量分数',

    -- 时间戳
    created_time DATETIME NOT NULL COMMENT '创建时间',
    updated_time DATETIME COMMENT '更新时间',
    completed_time DATETIME COMMENT '完成时间',

    INDEX idx_user_status (user_id, status),
    INDEX idx_created_time (created_time),
    INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='任务表';

索引设计思考

  • idx_user_status:用户查询自己的任务(WHERE user_id = ? AND status = ?)
  • idx_created_time:按时间排序(ORDER BY created_time DESC)
  • idx_status:调度器查询待处理任务(WHERE status = 'PENDING')

2. tbl_user(用户表)

CREATE TABLE tbl_user (
    id VARCHAR(36) PRIMARY KEY,
    username VARCHAR(50) UNIQUE NOT NULL,
    password VARCHAR(100) NOT NULL COMMENT 'BCrypt加密',
    email VARCHAR(100),
    role VARCHAR(20) DEFAULT 'USER' COMMENT 'USER/ADMIN',
    created_time DATETIME NOT NULL,
    last_login_time DATETIME,

    INDEX idx_username (username)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

3. tbl_subtitle(字幕表)

CREATE TABLE tbl_subtitle (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    task_id VARCHAR(36) NOT NULL,
    subtitle_index INT NOT NULL COMMENT '字幕序号',
    original_text TEXT COMMENT '原始文本',
    calibrated_text TEXT COMMENT '校准后文本',
    start_time DECIMAL(10,3) COMMENT '开始时间(秒)',
    end_time DECIMAL(10,3) COMMENT '结束时间(秒)',
    match_level INT COMMENT '匹配级别:1-6',

    INDEX idx_task_id (task_id),
    INDEX idx_task_index (task_id, subtitle_index)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

技术选型的思考

为什么选择Spring Boot?

优势

  1. [OK] 生态成熟:Spring Security、Spring Data JPA开箱即用
  2. [OK] 开发效率高:注解驱动,减少样板代码
  3. [OK] 易于测试:依赖注入,便于Mock
  4. [OK] 社区活跃:问题容易找到解决方案

劣势(我们可以接受):

  • [WARN] 启动速度慢(15-20秒)- 我们是长期运行的服务,不频繁重启
  • [WARN] 内存占用大(500MB+)- 服务器资源充足

为什么选择Python做AI服务?

优势

  1. [OK] Spacy是NLP领域最成熟的库
  2. [OK] NumPy/Pandas数据处理强大
  3. [OK] 算法工程师熟悉Python
  4. [OK] 与Java解耦,独立迭代

如果用Java实现NLP

  • [NO] 需要引入deeplearning4j等库,生态不如Python成熟
  • [NO] 团队学习成本高

为什么选择AWS S3?

对比方案

方案 优点 缺点 选择
本地文件系统 免费,简单 无法扩展,单点故障 [NO]
自建文件服务器 可控性强 运维成本高 [NO]
阿里云OSS 国内速度快 客户在美国 [NO]
AWS S3 稳定可靠,CDN加速 按量付费 [OK]

成本计算

  • 存储:$0.023/GB/月
  • 请求:$0.0004/1000次GET请求
  • 传输:前100GB免费

预估:每月处理1000个任务(每个100MB)= 100GB = $2.3/月

相比自建文件服务器

  • 自建:服务器$50/月 + 运维时间$200/月 = $250/月
  • S3:$2.3/月
  • 节省:$247.7/月(99%)

架构的扩展性

当前架构(单机部署)

┌─────────────────────────┐
│  服务器 (4核8G)          │
│  ├─ chronos-web-after   │
│  ├─ chronos-web-front   │
│  ├─ MySQL              │
│  └─ Redis              │
└─────────────────────────┘

┌─────────────────────────┐
│  服务器 (8核16G)         │
│  ├─ chronos-python      │
│  └─ chronos-translate   │
└─────────────────────────┘

支持规模:2000任务/天

扩展方案(水平扩展)

         ┌─── Nginx 负载均衡 ───┐
         │                      │
    ┌────┴────┐          ┌──────┴────┐
    │ Web服务1 │          │  Web服务2  │
    └─────────┘          └───────────┘
         │                      │
         └──────┬───────────────┘
                │
         ┌──────┴───────┐
         │  Redis集群   │
         └──────────────┘
                │
    ┌───────────┼───────────┐
    │           │           │
┌───┴──┐   ┌───┴──┐   ┌───┴──┐
│Python1│   │Python2│   │Python3│  ← 可独立扩展
└───────┘   └───────┘   └───────┘

支持规模:1000任务/天

扩展成本

  • 增加1台Python服务器:$100/月
  • 处理能力提升:3倍

总结

架构设计的核心思想

  1. 简单优于复杂

    • 不盲目追求微服务"正统"(Spring Cloud全家桶)
    • 用Redis而非RabbitMQ
    • HTTP REST而非gRPC
  2. 技术服务于业务

    • Java做业务编排(擅长)
    • Python做AI算法(擅长)
    • 各取所长,不强行统一
  3. 按需扩展

    • 当前规模用单机
    • 未来可以水平扩展
    • 架构有弹性
  4. 成本可控

    • S3比自建文件服务器便宜99%
    • Redis比RabbitMQ运维成本低
    • Selenium爬取降低翻译成本

架构的不足与改进方向

当前不足

  1. ⚠️ 服务间无服务发现(硬编码IP)
  2. ⚠️ 无分布式追踪(调试困难)
  3. ⚠️ 无熔断降级(雪崩风险)

改进方向(当规模扩大时):

  1. 引入Nacos做配置中心和服务发现
  2. 引入SkyWalking做分布式追踪
  3. 引入Sentinel做熔断降级

但现在不需要,因为:

  • 服务数量少(4个)
  • 部署规模小(2台服务器)
  • 团队规模小(3人)

过度设计就是浪费!


下期

下一篇文章,我将深入讲解系统的核心算法

《智能字幕校准系统实战(二):6级智能校准算法深度解析》

内容包括:

  • 字幕时间轴漂移的本质原因
  • Level 1-6匹配算法的详细实现
  • Spacy语义相似度计算原理
  • 箱线图异常检测算法
  • 算法参数调优经验

这是整个系列中技术难度最高、最有价值的一篇,敬请期待!


系列导航

  • 第0篇:系列开篇
  • 第1篇:微服务架构设计(当前)
  • 第2篇:6级智能校准算法深度解析
  • 第3篇:基于Spacy的多语言NLP处理实践
  • 第4篇:Spring Boot异步任务处理架构
  • 第5篇:多家STT/翻译服务集成方案
  • 第6篇:大文件处理与性能优化实战

原创不易,如果觉得有收获,请给个三连支持!

标签:# #Spring Boot #系统设计 #技术选型 #实战项目

posted @ 2025-11-07 10:19  0小豆0  阅读(8)  评论(0)    收藏  举报
隐藏
对话