space-test / ARCHITECTURE.md
fdwei4
init: Spring Boot demo with MySQL + Redis
49c44b0
|
Raw History Blame Contribute Delete
11.2 kB

零成本生产级 Spring Boot 部署架构

基于 HuggingFace Space Docker + 免费基础设施的完整方案。

架构拓扑

用户
  ↓
CloudFlare(自定义域名 + DDNS + CDN + WAF)
  ↓
HuggingFace Space Docker(Spring Boot 容器,2 vCPU / 16GB RAM)
  ↓
  ├─→ SQLPub MySQL(免费 500MB 存储 / 100 连接)
  └─→ Upstash Redis(免费 10K 命令/天 / 256MB)

关键特性:

  • 零费用:HF Space 免费持久 Docker 容器(非 Serverless,24x7 运行);数据库 / Redis / 域名全免费档
  • 真实公网 IP + 自定义域名:通过 CloudFlare DDNS 把 Space 的动态 .hf.space 域名映射到你的域名
  • 生产可用性:500MB MySQL(小应用够用)、Redis 缓存、CloudFlare CDN + WAF

组件清单与注册

组件 用途 免费额度 注册入口
HuggingFace Space 容器运行时 2 vCPU + 16GB RAM 持久容器 https://huggingface.co/new-space
SQLPub MySQL 关系数据库 500MB 存储 / 100 并发连接 https://console.sqlpub.com/user/login
Upstash Redis 缓存 / Session 10K 命令/天 / 256MB 内存 https://console.upstash.com/auth/sign-in
CloudFlare 域名解析 + CDN + DDNS 无限流量 / 免费 SSL https://dash.cloudflare.com/

详细配置步骤

1. SQLPub MySQL

  1. 注册 https://console.sqlpub.com/ → 创建数据库(选 MySQL 5.7+)
  2. 在控制台获取连接信息:
    主机:<host>.sqlpub.com
    端口:3306
    数据库名:<dbname>
    用户名:<username>
    密码:<password>
    
  3. JDBC URL(供 Spring Boot 用):
    jdbc:mysql://<host>.sqlpub.com:3306/<dbname>?useSSL=true&serverTimezone=UTC
    

限制:

  • 500MB 存储(约 100 万小条记录)
  • 100 并发连接(对单容器应用足够)
  • 外网访问慢(约 100-300ms 延迟);如需低延迟,考虑付费升级或自建

2. Upstash Redis

  1. 注册 https://console.upstash.com/ → Create Database → 选 Global(最低延迟)
  2. 拿连接信息:
    UPSTASH_REDIS_REST_URL=https://xxx.upstash.io
    UPSTASH_REDIS_REST_TOKEN=AXXXxxxxx
    
  3. Spring Boot 集成:用 Jedis 或 Lettuce 客户端,配置 host/port/password(见下方 application.yml)

限制:

  • 10,000 命令/天(约每 8.6 秒一条,对低频应用够用)
  • 256MB 内存
  • 超额后节流(不会直接拒绝,但会变慢)

优化建议:

  • Session 存 Redis 时设 TTL(如 30 分钟),自动过期省配额
  • 低频场景(如缓存配置、限流计数器)适合;高频实时计算不适合

3. CloudFlare 域名 + DDNS

3.1 域名托管到 CloudFlare

  1. 在域名注册商(如 Namesilo / Porkbun)购买域名(约 $2-10/年)
  2. 登录 https://dash.cloudflare.com/ → Add Site → 输入域名
  3. 按提示修改域名 NS 记录指向 CloudFlare(如 ns1.cloudflare.com / ns2.cloudflare.com)

3.2 DDNS(动态更新 HF Space IP)

原理:HF Space 的公网地址是 <用户名>-<空间名>.hf.space(动态,可能变化)。通过 CloudFlare API + DDNS 脚本,定期解析 .hf.space 的真实 IP 并更新到你的域名 A 记录,实现自定义域名访问。

参考方案(博客详解):

核心步骤:

  1. 在 CloudFlare 获取 Zone ID 和 API Token(权限:Zone.DNS Edit)
  2. 在 HF Space 容器内 or 定时任务服务器(如 GitHub Actions / 自己的服务器)跑 DDNS 脚本:
    #!/bin/bash
    DOMAIN="yourdomain.com"
    SUBDOMAIN="api"  # 最终域名 api.yourdomain.com
    HF_SPACE_URL="<用户名>-<空间名>.hf.space"
    
    # 解析 HF Space 当前 IP
    CURRENT_IP=$(dig +short "$HF_SPACE_URL" | tail -1)
    
    # 调 CloudFlare API 更新 A 记录
    curl -X PUT "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$RECORD_ID" \
      -H "Authorization: Bearer $CF_API_TOKEN" \
      -H "Content-Type: application/json" \
      --data "{\"type\":\"A\",\"name\":\"$SUBDOMAIN\",\"content\":\"$CURRENT_IP\",\"ttl\":120}"
    
  3. 定时执行(如 cron 每 5 分钟):HF Space IP 变化后自动同步

优势:

  • CloudFlare 免费 CDN + DDoS 防护 + 免费 SSL
  • 自定义域名(api.yourdomain.com)比 .hf.space 更专业

4. HuggingFace Space 环境变量

敏感信息(数据库密码 / Redis token)不能硬编码,用 Space Secrets:

  1. 进入 Space 设置页 → Repository secrets
  2. 添加变量(Spring Boot 会通过环境变量读取):
    DB_HOST=<host>.sqlpub.com
    DB_PORT=3306
    DB_NAME=<dbname>
    DB_USER=<username>
    DB_PASSWORD=<password>
    
    REDIS_HOST=<xxx>.upstash.io
    REDIS_PORT=6379
    REDIS_PASSWORD=<token>
    
    CF_ZONE_ID=<zone_id>
    CF_API_TOKEN=<api_token>
    CF_RECORD_ID=<dns_record_id>
    

Spring Boot 配置示例

src/main/resources/application.yml:

spring:
  application:
    name: hf-springboot-demo
  
  # SQLPub MySQL
  datasource:
    url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:test}?useSSL=true&serverTimezone=UTC&characterEncoding=utf8
    username: ${DB_USER:root}
    password: ${DB_PASSWORD:password}
    driver-class-name: com.mysql.cj.jdbc.Driver
    hikari:
      maximum-pool-size: 10      # SQLPub 免费限 100 连接,单容器 10 足够
      connection-timeout: 30000
  
  # JPA(可选,如果用 ORM)
  jpa:
    hibernate:
      ddl-auto: update            # 生产改 validate
    show-sql: false
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL8Dialect
  
  # Upstash Redis
  redis:
    host: ${REDIS_HOST:localhost}
    port: ${REDIS_PORT:6379}
    password: ${REDIS_PASSWORD:}
    timeout: 5000
    lettuce:
      pool:
        max-active: 8             # 连接池大小
        max-idle: 4
        min-idle: 1

server:
  port: 7860                      # HF Space 要求
  address: 0.0.0.0

依赖(pom.xml 新增):

<!-- MySQL -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.33</version>
</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>

DDNS 自动更新脚本(容器内定时任务)

src/main/java/com/example/hfspace/task/CloudFlareDDNSTask.java:

package com.example.hfspace.task;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.net.InetAddress;
import java.util.HashMap;
import java.util.Map;

@Component
public class CloudFlareDDNSTask {

    @Value("${cf.zone-id:}")
    private String zoneId;

    @Value("${cf.api-token:}")
    private String apiToken;

    @Value("${cf.record-id:}")
    private String recordId;

    @Value("${hf.space-url:}")
    private String hfSpaceUrl;

    private final RestTemplate restTemplate = new RestTemplate();

    // 每 5 分钟检查一次
    @Scheduled(fixedRate = 300000)
    public void updateDNS() {
        if (zoneId.isEmpty() || hfSpaceUrl.isEmpty()) {
            return; // 未配置,跳过
        }

        try {
            // 解析 HF Space 当前 IP
            String currentIP = InetAddress.getByName(hfSpaceUrl).getHostAddress();

            // 调 CloudFlare API 更新
            String url = String.format("https://api.cloudflare.com/client/v4/zones/%s/dns_records/%s", 
                                       zoneId, recordId);
            
            Map<String, Object> body = new HashMap<>();
            body.put("type", "A");
            body.put("content", currentIP);
            body.put("ttl", 120);

            var headers = new org.springframework.http.HttpHeaders();
            headers.set("Authorization", "Bearer " + apiToken);
            headers.set("Content-Type", "application/json");

            var entity = new org.springframework.http.HttpEntity<>(body, headers);
            restTemplate.exchange(url, org.springframework.http.HttpMethod.PUT, entity, String.class);

            System.out.println("DDNS updated: " + currentIP);
        } catch (Exception e) {
            System.err.println("DDNS update failed: " + e.getMessage());
        }
    }
}

启用定时任务(主类加 @EnableScheduling):

@SpringBootApplication
@EnableScheduling
public class HfSpaceApplication {
    public static void main(String[] args) {
        SpringApplication.run(HfSpaceApplication.class, args);
    }
}

部署流程

  1. 本地测试:

    export DB_HOST=<host>.sqlpub.com
    export DB_USER=<user>
    export DB_PASSWORD=<password>
    export REDIS_HOST=<xxx>.upstash.io
    export REDIS_PASSWORD=<token>
    mvn spring-boot:run
    
  2. 推送到 HF Space:

    git clone https://huggingface.co/spaces/<用户名>/<空间名>
    # 拷贝所有文件(Dockerfile / pom.xml / src / README.md)
    git add .
    git commit -m "add mysql/redis integration + DDNS"
    git push
    
  3. 配置 Space Secrets:进入 Space 设置页添加环境变量(DB_HOST / DB_PASSWORD / REDIS_HOST 等)

  4. 验证:

    • 访问 https://<用户名>-<空间名>.hf.space/api/hello 应返回 JSON
    • 日志里看到 "DDNS updated: x.x.x.x" 说明定时任务工作
    • 几分钟后通过自定义域名 https://api.yourdomain.com/api/hello 也能访问

限制与成本边界

维度 免费额度 突破方式
HF Space CPU/RAM 2 vCPU + 16GB(持久容器) 升级 Pro($9/月,8 vCPU + 64GB)
MySQL 存储 500MB SQLPub 付费版 or 迁移 PlanetScale / Supabase(也有免费档)
Redis 命令数 10K/天(≈ 0.12 QPS) Upstash 付费版 or 自建 Redis(需额外服务器)
域名 年付 $2-10 续费即可
流量 CloudFlare 无限 N/A

适用场景:

  • 个人项目 / MVP / 低流量 API(< 1K DAU)
  • 学习 / 展示 / 开源项目托管
  • 不适合:高并发生产(DB/Redis 配额不够)、对数据安全性要求极高的场景(托管服务风险)

进阶优化

  1. 监控:集成 Sentry(错误追踪)+ Uptime Robot(健康检查)
  2. CI/CD:GitHub Actions 自动推送到 HF Space(避免手动 git push)
  3. 多环境:本地 dev 用 H2 内存库,Space 用 MySQL(通过 Spring Profile 切换)
  4. 备份:定期 mysqldump 导出 SQLPub 数据到 GitHub private repo

参考链接