File size: 11,242 Bytes
49c44b0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
# 零成本生产级 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 记录,实现自定义域名访问。

**参考方案**(博客详解):
- https://blog.qjyg.de/post/tech/domain-dpdns/

**核心步骤**:

1. 在 CloudFlare 获取 **Zone ID** 和 **API Token**(权限:Zone.DNS Edit)
2. 在 **HF Space 容器内** or **定时任务服务器**(如 GitHub Actions / 自己的服务器)跑 DDNS 脚本:
   ```bash
   #!/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`:

```yaml
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` 新增)**:

```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`:

```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`):

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

## 部署流程

1. **本地测试**:
   ```bash
   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**:
   ```bash
   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

## 参考链接

- SQLPub 控制台:https://console.sqlpub.com/
- Upstash Redis:https://console.upstash.com/
- CloudFlare Dashboard:https://dash.cloudflare.com/
- CloudFlare DDNS 博客:https://blog.qjyg.de/post/tech/domain-dpdns/
- HuggingFace Spaces Docs:https://huggingface.co/docs/hub/spaces-sdks-docker