Appearance
Go 服务端示例
本示例展示如何在 Go 服务端实现定时拉取配置、缓存密文、并向客户端提供接口。
示例使用标准库 + go-redis,可根据实际技术栈调整。
接口域名见服务端接入说明 · 环境说明。
依赖
bash
go get github.com/redis/go-redis/v9完整示例
go
package main
import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"time"
"github.com/redis/go-redis/v9"
)
const (
configAPI = "https://api.dc-server.cc/sdk/getAvailableDomainList"
cacheKey = "sdk:encrypted_config"
cacheTTL = 30 * time.Minute // Redis TTL:30 分钟(拉取间隔的 3 倍)
fetchInterval = 10 * time.Minute
)
var rdb *redis.Client
func init() {
rdb = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
}
// fetchAndCache 拉取配置并缓存
func fetchAndCache(ctx context.Context) {
client := &http.Client{Timeout: 5 * time.Second}
resp, err := client.Get(configAPI)
if err != nil {
log.Printf("[SdkConfig] 拉取异常,沿用上次缓存: %v", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data string `json:"data"`
}
if err := json.Unmarshal(body, &result); err != nil {
log.Printf("[SdkConfig] 响应解析失败,沿用上次缓存: %v", err)
return
}
if result.Code == 0 && result.Data != "" {
// 成功:更新缓存,重置 TTL
if err := rdb.Set(ctx, cacheKey, result.Data, cacheTTL).Err(); err != nil {
log.Printf("[SdkConfig] Redis 写入失败: %v", err)
return
}
log.Println("[SdkConfig] 拉取成功,已更新缓存")
} else {
// 失败:保留上次缓存,不清空
log.Printf("[SdkConfig] 拉取失败,code=%d,沿用上次缓存", result.Code)
}
}
// startScheduler 启动定时任务
func startScheduler(ctx context.Context) {
// 启动时立即拉取一次
fetchAndCache(ctx)
ticker := time.NewTicker(fetchInterval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
fetchAndCache(ctx)
case <-ctx.Done():
return
}
}
}
// getSdkConfigHandler 向客户端提供密文
func getSdkConfigHandler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
w.Header().Set("Content-Type", "application/json")
encryptedData, err := rdb.Get(ctx, cacheKey).Result()
if err == redis.Nil || encryptedData == "" {
// 无缓存:返回错误状态
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `{"code":-1,"msg":"config not available"}`)
return
}
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, `{"code":-1,"msg":"internal error"}`)
return
}
// 密文原样返回,不解密
resp := map[string]interface{}{
"code": 0,
"msg": "success",
"data": encryptedData,
}
json.NewEncoder(w).Encode(resp)
}
func main() {
ctx := context.Background()
// 启动定时拉取任务(后台 goroutine)
go startScheduler(ctx)
// 注册 HTTP 路由
http.HandleFunc("/your-api/sdk-config", getSdkConfigHandler)
log.Println("服务启动,监听 :8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}关键说明
- 不解密
data:服务端只存储密文字符串,不尝试解密 - 失败兜底:拉取失败(网络异常、非 0 code)时不清空 Redis 缓存,沿用上次密文
- TTL 兜底:Redis TTL 设为 30 分钟,防止极端情况下缓存意外过期
- 启动时立即拉取:
startScheduler启动后先执行一次拉取,不等第一个 tick - 5 秒超时:HTTP 客户端设置超时,避免阻塞