Appearance
Java 服务端示例
本示例展示如何在 Java 服务端实现定时拉取配置、缓存密文、并向客户端提供接口。
示例使用 Spring Boot + Hutool HttpUtil + Redis,可根据实际技术栈调整。
接口域名见服务端接入说明 · 环境说明。
依赖
xml
<!-- pom.xml -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.25</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>定时拉取配置
java
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class SdkConfigScheduler {
private static final String CONFIG_API = "https://api.dc-server.cc/sdk/getAvailableDomainList";
private static final String CACHE_KEY = "sdk:encrypted_config";
// Redis TTL:30 分钟(拉取间隔的 3 倍,作为兜底)
private static final long CACHE_TTL_MINUTES = 30;
private final StringRedisTemplate redisTemplate;
public SdkConfigScheduler(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 每 10 分钟拉取一次配置
*/
@Scheduled(fixedDelay = 10 * 60 * 1000)
public void fetchAndCache() {
try {
String responseBody = HttpUtil.get(CONFIG_API, 5000); // 5s 超时
JSONObject json = JSONUtil.parseObj(responseBody);
if (json.getInt("code") == 0) {
String encryptedData = json.getStr("data");
// 成功:更新缓存,重置 TTL
redisTemplate.opsForValue().set(CACHE_KEY, encryptedData, CACHE_TTL_MINUTES, TimeUnit.MINUTES);
System.out.println("[SdkConfig] 拉取成功,已更新缓存");
} else {
// 失败:保留上次缓存,不清空
System.err.println("[SdkConfig] 拉取失败,code=" + json.getInt("code") + ",沿用上次缓存");
}
} catch (Exception e) {
// 异常:保留上次缓存,不清空
System.err.println("[SdkConfig] 拉取异常,沿用上次缓存: " + e.getMessage());
}
}
}向客户端提供接口
java
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
public class SdkConfigController {
private static final String CACHE_KEY = "sdk:encrypted_config";
private final StringRedisTemplate redisTemplate;
public SdkConfigController(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 客户端调用此接口获取密文
* GET /your-api/sdk-config
*/
@GetMapping("/your-api/sdk-config")
public ResponseEntity<Map<String, Object>> getSdkConfig() {
Map<String, Object> result = new HashMap<>();
String encryptedData = redisTemplate.opsForValue().get(CACHE_KEY);
if (encryptedData != null && !encryptedData.isEmpty()) {
result.put("code", 0);
result.put("msg", "success");
result.put("data", encryptedData); // 密文原样返回,不解密
return ResponseEntity.ok(result);
} else {
result.put("code", -1);
result.put("msg", "config not available");
return ResponseEntity.ok(result);
}
}
}启用定时任务
在 Spring Boot 启动类上添加注解:
java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}关键说明
- 不解密
data:服务端只存储密文字符串,不尝试解密 - 失败兜底:拉取失败时不清空缓存,沿用上次成功的密文
- TTL 兜底:Redis TTL 设为 30 分钟,防止极端情况下缓存意外过期
- 5 秒超时:HTTP 请求设置超时,避免定时任务被长时间阻塞