共计 2432 个字符,预计需要花费 7 分钟才能阅读完成。
背景痛点:为什么天气 Skill 需要特殊设计?
天气类技能在突发天气事件(如暴雨预警、台风警报)时,往往面临两大核心挑战:

- API 调用暴涨 :当极端天气发生时,用户查询量可能瞬间增长 10 倍以上
- 数据时效性 :气温、降水概率等数据需要保持 5 分钟内的更新精度
我们曾遇到真实案例:某次台风登陆时,传统单体架构的服务因数据库连接池耗尽导致整个系统瘫痪。这正是我们需要微服务化改造的根本原因。
架构选型:为什么选择 Spring Cloud + Redis?
单体架构 vs 微服务架构对比
| 维度 | 单体架构 | 微服务架构 |
|---|---|---|
| 扩展性 | 垂直扩展成本高 | 可按天气 API 独立水平扩展 |
| 容错能力 | 单点故障影响全局 | 服务隔离,故障自动熔断 |
| 部署效率 | 简单但迭代周期长 | 独立部署,灰度发布灵活 |
技术栈决策依据
- Spring Cloud:
- 成熟的微服务生态(服务发现、配置中心、熔断器)
-
与 Spring Boot 的无缝集成
-
Redis:
- 读写性能达到 10 万 QPS 级别
- 原生支持过期时间和发布订阅
核心实现:三大关键技术方案
1. 熔断降级(Circuit Breaker)实现
使用 FeignClient 集成 Hystrix,当天气 API 不可用时自动返回缓存数据:
@FeignClient(name = "weather-api", fallback = WeatherFallback.class)
public interface WeatherClient {@GetMapping("/current/{cityCode}")
WeatherDTO getCurrentWeather(@PathVariable String cityCode);
}
@Component
public class WeatherFallback implements WeatherClient {
@Autowired
private RedisTemplate<String, WeatherDTO> redisTemplate;
@Override
public WeatherDTO getCurrentWeather(String cityCode) {return redisTemplate.opsForValue().get("weather:" + cityCode);
}
}
2. 智能缓存策略
采用多级缓存设计(Redis + 本地缓存):
- 缓存更新 :通过 Spring Scheduler 定时更新
- TTL 设置 :基础数据 30 分钟,预警数据 5 分钟
- 缓存预热 :在低峰期预加载热点城市数据
@Scheduled(fixedRate = 300_000) // 5 分钟更新
public void refreshWeatherCache() {List<String> hotCities = getHotCities(); // 获取高频查询城市
hotCities.forEach(city -> {WeatherDTO freshData = fetchFromAPI(city);
redisTemplate.opsForValue().set(
"weather:" + city,
freshData,
// 随机过期时间防雪崩
30 + ThreadLocalRandom.current().nextInt(10),
TimeUnit.MINUTES);
});
}
3. 请求合并(Hystrix Collapser)
对 10ms 内的相同城市请求自动合并,减少 API 调用次数:
@HystrixCollapser(
batchMethod = "batchGetWeather",
scope = GLOBAL,
collapserProperties = @HystrixProperty(name = "timerDelayInMilliseconds", value = "10")
)
public Future<WeatherDTO> getWeatherCollapsed(String cityCode) {return null; // 实际由框架实现}
@HystrixCommand
public List<WeatherDTO> batchGetWeather(List<String> cityCodes) {return weatherApi.batchQuery(cityCodes);
}
避坑指南:血泪经验总结
缓存雪崩防护
- 永远不要设置固定过期时间
- 推荐采用:基础过期时间 + 随机偏移量
API 配额管理
- 使用 Guava RateLimiter 做客户端限流
- 监控每日用量,达到 90% 阈值时触发告警
- 建立备用 API 供应商切换机制
地理位置处理
- 城市级别:使用高德 / 百度地图 API 转换坐标
- 精确到区县:建立本地 GIS 数据库
- 容错方案:当 GPS 精度不足时,默认返回城市中心天气
性能验证:压测数据对比
使用 JMeter 模拟 1 万并发用户:
| 优化措施 | QPS | 99 线延迟 | 错误率 |
|---|---|---|---|
| 原始方案 | 1,200 | 850ms | 8.7% |
| 增加缓存 | 5,800 | 210ms | 0.3% |
| 请求合并 + 熔断 | 9,400 | 110ms | 0.1% |
代码规范建议
- 所有 DTO 类必须实现 Serializable
- Redis 的 Key 命名统一使用冒号分隔
- 关键方法必须包含 JavaDoc:
/**
* 获取混合天气数据(实时 + 预报)* @param cityCode 国家行政区划代码
* @param days 预报天数(1-7)* @throws ApiException 当 API 不可用且无缓存时抛出
*/
public WeatherDTO getHybridWeather(String cityCode, int days) {// ...}
思考题:增量同步设计
当需要支持跨国天气数据时,如何设计增量同步机制?考虑:
- 如何识别数据变更(版本号?时间戳?)
- 如何处理时区差异
- 如何保证数据传输效率
欢迎在评论区分享你的设计方案!
结语
通过这套架构,我们的天气 Skill 在 618 大促期间平稳支撑了每秒 1.2 万次的查询请求。关键经验是:
- 微服务化是应对突发流量的基础
- 缓存策略需要根据数据特性动态调整
- 永远要有降级方案
下一步计划探索边缘计算,将天气数据推到 CDN 边缘节点,进一步降低延迟。如果你有相关经验,欢迎交流讨论!
正文完
发表至: 技术开发
近一天内
