实现异步天气数据获取与Spring缓存集成

你好呀,我是小邹。

在Web应用中,实时天气数据的获取是一个常见的需求,特别是在需要频繁更新天气信息的场景下,如旅游网站、天气应用或任何需要展示地理位置相关天气的应用。然而,频繁的外部API调用不仅会增加服务器的负担,还可能导致网络延迟,影响用户体验。为了优化这一过程,本文将介绍如何在Spring Boot应用中利用异步调用和缓存技术来高效地获取并存储天气数据。

效果图

引入Spring异步支持

在Spring框架中,可以使用@Async注解来指定方法应该异步执行。这允许应用在等待耗时操作(如网络调用)完成的同时继续处理其他请求,从而提高了整体的响应性和吞吐量。

利用Spring Cache缓存结果

为了减少对远程API的重复调用,我们可以使用Spring Cache框架来缓存天气数据。@Cacheable注解可以自动存储方法的返回值,如果下次请求相同的数据,它将从缓存中读取,而不是重新执行方法。

代码实现

以下代码示例展示了如何在Spring Boot中结合使用@Async和@Cacheable注解来异步获取并缓存天气数据:

java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.ui.Model;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.data.util.Pair;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.collection.CollUtil;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.time.LocalDateTime;

@Service
public class WeatherService {

    private static final String WEATHER_CACHE_NAME = "weatherCache";
    private static final String GAO_DE_KEY = "YOUR_GAODE_API_KEY";
    private final RestTemplate restTemplate;

    @Autowired
    public WeatherService(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @Async
    @Cacheable(value = WEATHER_CACHE_NAME, key = "'todayWeather'")
    public void getWeather(Model model) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HashMap<String, String> weatherMap = new HashMap<>();

        // 获取客户端IP地址
        String ip = IpParseUtil.getIpAddr(request);
        List<String> address = IpParseUtil.parse(ip, null);
        String region = "";
        if (address.size() > 3) {
            region = address.get(2) + address.get(3);
        }

        // 地理位置编码
        Map<String, Object> ipInfoMap = restTemplate.getForObject("https://restapi.amap.com/v3/geocode/geo?key=" + GAO_DE_KEY + "&output=json&address=" + region, Map.class);
        List<Object> geocodes = (List<Object>) ipInfoMap.get("geocodes");
        String adCode = (String) ((LinkedHashMap) geocodes.get(0)).get("adcode");

        // 查询天气信息
        Map<String, Object> weatherMapResponse = restTemplate.getForObject("https://restapi.amap.com/v3/weather/weatherInfo?city=" + adCode + "&key=" + GAO_DE_KEY + "&extensions=all", Map.class);
        
        // 处理天气信息
        processWeatherResponse(weatherMapResponse, weatherMap);

        // 将天气数据添加到模型中
        model.addAttribute("weather", weatherMap);
    }

    private void processWeatherResponse(Map<String, Object> weatherMapResponse, HashMap<String, String> weatherMap) {
        if (CollUtil.isEmpty((Collection<?>) weatherMapResponse.get("forecasts"))) {
            // 如果无法获取天气信息,设置默认值
            weatherMap.put("city", "获取定位失败");
            weatherMap.put("weather", "*");
            weatherMap.put("temperature", "*");
            weatherMap.put("reportTime", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            weatherMap.put("tomorrowWeather", "*");
            weatherMap.put("tomorrowTemperature", "*");
            weatherMap.put("tomorrowLowTemperature", "*");
        } else {
            // 提取并处理天气信息
            List<Map<String, Object>> forecasts = (List<Map<String, Object>>) weatherMapResponse.get("forecasts");
            String city = forecasts.get(0).get("city").toString().replaceAll("市$", "");
            weatherMap.put("city", city);

            // 实时天气
            Map<String, Object> liveWeather = (Map<String, Object>) forecasts.get(0).get("lives");
            weatherMap.put("weather", (String) liveWeather.get("weather"));
            weatherMap.put("temperature", (String) liveWeather.get("temperature"));
            weatherMap.put("reportTime", (String) liveWeather.get("reporttime"));

            // 天气预报
            List<Map<String, Object>> forecast = (List<Map<String, Object>>) forecasts.get(0).get("casts");
            weatherMap.put("tomorrowWeather", (String) forecast.get(1).get("dayweather"));
            weatherMap.put("tomorrowTemperature", (String) forecast.get(1).get("daytemp"));
            weatherMap.put("tomorrowLowTemperature", (String) forecast.get(1).get("nighttemp"));
        }
    }
}
注意事项
  • 以上代码示例中,IpParseUtil是一个自定义的工具类,用于解析IP地址并获取地理位置信息。你需要根据实际情况实现或替换这部分逻辑。
  • GAO_DE_KEY应该替换为你的高德地图API密钥。
  • RestTemplate用于发起HTTP请求,你可以选择使用WebClient或任何其他HTTP客户端库作为替代。
  • 确保你的Spring Boot项目中已经包含了Spring Cache和Spring Web的支持。

通过以上步骤,你可以在Spring Boot应用中实现异步天气数据获取并利用缓存来优化性能。这不仅可以提升用户体验,还能降低服务器负载,提高应用的整体效率。

相关推荐
一枕槐安4871 小时前
RAG 服务扛住高并发:缓存、队列、限流三件套
java·spring·缓存·异步任务·rag·断路器
YDS8294 小时前
Small Spring IOC篇:作用域和FactoryBean
java·后端·spring
雷一言说4 小时前
Spring AI 2.0 Modular RAG:接入 Rerank 重排模型
spring
学到老活到老5 小时前
第 22 集:Filter、Interceptor、AOP 都能拦截,为什么不能互换?
java·后端·spring
郭小李5 小时前
spring 中的 @EnableWebMvc注解和WebMvcConfigurer接口
java·后端·spring
CoderYanger6 小时前
Java EE 进阶:5. Spring IoC&DI(第二弹):Spring IoC
java·开发语言·spring boot·spring·面试·职场和发展·java-ee
霸道流氓气质7 小时前
AI 翻译插件:翻译记忆级联复用、术语混合检索与结构化质量门禁的 Vue3 + Spring AI 生产级实战
java·人工智能·spring
霸道流氓气质9 小时前
智能表单生成器:结构化输出契约、JSON Schema 驱动渲染与 EAV/JSONB 混合存储的 Vue3 + Spring AI 生产级实战
人工智能·spring·json
与数据交流的路上10 小时前
一次 redis 幽灵内存的分析
数据库·redis·缓存
Geek-Chow10 小时前
06 · DeepSeek V4.1 Flash 推理端到端走查:一个 Agent 请求穿过 recipe、KV 缓存、Prefill 与 Decode
缓存