实现异步天气数据获取与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应用中实现异步天气数据获取并利用缓存来优化性能。这不仅可以提升用户体验,还能降低服务器负载,提高应用的整体效率。

相关推荐
hqxstudying11 分钟前
java依赖注入方法
java·spring·log4j·ioc·依赖
春生野草38 分钟前
关于SpringMVC的整理
spring
Bug退退退1231 小时前
RabbitMQ 高级特性之重试机制
java·分布式·spring·rabbitmq
hello早上好3 小时前
CGLIB代理核心原理
java·spring
在肯德基吃麻辣烫3 小时前
《Redis》缓存与分布式锁
redis·分布式·缓存
先睡9 小时前
Redis的缓存击穿和缓存雪崩
redis·spring·缓存
Bug退退退12313 小时前
RabbitMQ 高级特性之死信队列
java·分布式·spring·rabbitmq
booooooty19 小时前
基于Spring AI Alibaba的多智能体RAG应用
java·人工智能·spring·多智能体·rag·spring ai·ai alibaba
极光雨雨19 小时前
Spring Bean 控制销毁顺序的方法总结
java·spring
Spirit_NKlaus19 小时前
解决HttpServletRequest无法获取@RequestBody修饰的参数
java·spring boot·spring