1.数据来源
-
天气的数据来源为中华万年历
通过城市名称获得天气数据:http://wthrcdn.etouch.cn/weather_mini?city=北京
通过城市id获得天气数据:http://wthrcdn.etouch.cn/weather_mini?citykey=101280601
-
城市id列表
每个城市都有一个唯一的id作为标识:https://waylau.com/data/citylist.xml
-
这里已北京为例,可以看到如下天气数据返回
{
"data": {
"yesterday": {
"date": "26日星期三",
"high": "高温 35℃",
"fx": "南风",
"low": "低温 23℃",
"fl": "<![CDATA[3-4级]]>",
"type": "多云"
},
"city": "北京",
"forecast": [{
"date": "27日星期四",
"high": "高温 33℃",
"fengli": "<![CDATA[<3级]]>",
"low": "低温 23℃",
"fengxiang": "北风",
"type": "多云"
}, {
"date": "28日星期五",
"high": "高温 33℃",
"fengli": "<![CDATA[<3级]]>",
"low": "低温 23℃",
"fengxiang": "南风",
"type": "多云"
}, {
"date": "29日星期六",
"high": "高温 36℃",
"fengli": "<![CDATA[<3级]]>",
"low": "低温 23℃",
"fengxiang": "东风",
"type": "多云"
}, {
"date": "30日星期天",
"high": "高温 32℃",
"fengli": "<![CDATA[3-4级]]>",
"low": "低温 22℃",
"fengxiang": "北风",
"type": "多云"
}, {
"date": "1日星期一",
"high": "高温 34℃",
"fengli": "<![CDATA[<3级]]>",
"low": "低温 23℃",
"fengxiang": "北风",
"type": "晴"
}],
"ganmao": "各项气象条件适宜,发生感冒机率较低。但请避免长期处于空调房间中,以防感冒。",
"wendu": "29"
},
"status": 1000,
"desc": "OK"
}
2.开发环境
-
JDK8
-
Maven
-
Spring Boot Web Starter 2.1.6
-
Apache HttpClient 4.5.3
3.初始化一个Spring Boot项目
(1)添加maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.7.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
(2)创建实体类
@Data
public class Weather implements Serializable {
private String city;
private String aqi;
private String wendu;
private String ganmao;
private Yesterday yesterday;
private List<Forecast> forecast;
}
@Data
public class Forecast implements Serializable {
private String date;
private String high;
private String fengxiang;
private String low;
private String fengli;
private String type;
}
@Data
public class Yesterday implements Serializable {
private String date;
private String high;
private String fx;
private String low;
private String fl;
private String type;
}
@Data
public class WeatherResponse implements Serializable {
/**
* 消息数据
*/
private Weather data;
/**
* 消息状态
*/
private String status;
/**
* 消息描述
*/
private String desc;
}
(3)服务接口及实现
public interface WeatherDataService {
/**
* 根据城市id来查询天气数据
*
* @param cityId
* @return
*/
WeatherResponse getDataByCityId(String cityId);
/**
* 根据城市名称来查询天气数据
*
* @param cityName
* @return
*/
WeatherResponse getDataByCityName(String cityName);
}
@Service
public class WeatherDataServiceImpl implements WeatherDataService {
@Autowired
private RestTemplate restTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;
private final Logger logger = LoggerFactory.getLogger(WeatherDataServiceImpl.class);
private final String WEATHER_API = "http://wthrcdn.etouch.cn/weather_mini";
/**
* 缓存超时时间
*/
private final Long TIME_OUT = 1800L;
@Override
public WeatherResponse getDataByCityId(String cityId) {
String uri = WEATHER_API + "?citykey=" + cityId;
return this.doGetWeatherData(uri);
}
@Override
public WeatherResponse getDataByCityName(String cityName) {
String uri = WEATHER_API + "?city=" + cityName;
return this.doGetWeatherData(uri);
}
private WeatherResponse doGetWeatherData(String uri) {
ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
//将调用的uri作为缓存的key
String strBody = null;
//先查缓存,没有找到查服务
if (!stringRedisTemplate.hasKey(uri)) {
logger.info("未找到 key " + uri);
ResponseEntity<String> response = restTemplate.getForEntity(uri, String.class);
if (response.getStatusCodeValue() == 200) {
strBody = response.getBody();
}
ops.set(uri, strBody, TIME_OUT, TimeUnit.SECONDS);
} else {
logger.info("找到 key " + uri + ",value=" + ops.get(uri));
strBody = ops.get(uri);
}
ObjectMapper mapper = new ObjectMapper();
WeatherResponse weather = null;
try {
weather = mapper.readValue(strBody, WeatherResponse.class);
} catch (IOException e) {
e.printStackTrace();
}
return weather;
}
}
(4)控制层类
@RestController
@RequestMapping("/weather")
public class WeatherController {
@Autowired
private WeatherDataService weatherDataService;
@GetMapping("/cityId/{cityId}")
public WeatherResponse getReportByCityId(@PathVariable("cityId") String cityId) {
return weatherDataService.getDataByCityId(cityId);
}
@GetMapping("/cityName/{cityName}")
public WeatherResponse getReportByCityName(@PathVariable("cityName") String cityName) {
return weatherDataService.getDataByCityName(cityName);
}
}
(5)配置类
@Configuration
public class Config {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
// Do any additional configuration here
return builder.build();
}
}
(6)Spring配置文件
spring:
redis:
host: 127.0.0.1
database: 0
password:
port: 6379
4.为什么使用Redis
缓存的使用与系统的时效性有着非常大的关系。当所使用的系统时效性要求不高时,选择使用缓存是极好的。当系统要求的时效性比较高时,则并不适合使用缓存。
天气数据接口,本身时效性并不是很高,而且又因为时Web服务,在调用过程中,是存在延时的。所以,采用缓存,一方面可以有效减轻访问天气接口服务带来的延时问题;另一方面也可以减轻天气接口的负担,提高并发访问量。
特别是使用第三方免费的天气API,这些API往往对用户的调用次数及频率有一定的限制。所以为了减轻天气API提供方的负荷,并不需要去实时调用其第三方接口。
由于天气数据更新频率的特点(基本上一个小时或半个小时更新一次),因此,在redis中设置了30分钟的失效时间。
5.测试和运行
1.启动Spring Boot项目,启动redis
2.访问接口:http://localhost:8080/weather/cityName/太原,日志如下
未找到 key http://wthrcdn.etouch.cn/weather_mini?city=太原
3.再次访问上面的接口,日志如下
找到 key http://wthrcdn.etouch.cn/weather_mini?city=太原,value={"data":{"yesterday":{"date":"26日星期三","high":"高温 32℃","fx":"东北风","low":"低温 20℃","fl":"<![CDATA[<3级]]>","type":"多云"},"city":"太原","forecast":[{"date":"27日星期四","high":"高温 28℃","fengli":"<![CDATA[<3级]]>","low":"低温 18℃","fengxiang":"北风","type":"小雨"},{"date":"28日星期五","high":"高温 32℃","fengli":"<![CDATA[<3级]]>","low":"低温 16℃","fengxiang":"东北风","type":"多云"},{"date":"29日星期六","high":"高温 32℃","fengli":"<![CDATA[<3级]]>","low":"低温 15℃","fengxiang":"西北风","type":"晴"},{"date":"30日星期天","high":"高温 30℃","fengli":"<![CDATA[<3级]]>","low":"低温 16℃","fengxiang":"北风","type":"晴"},{"date":"1日星期一","high":"高温 31℃","fengli":"<![CDATA[<3级]]>","low":"低温 15℃","fengxiang":"东南风","type":"多云"}],"ganmao":"相对今天出现了较大幅度降温,较易发生感冒,体质较弱的朋友请注意适当防护。","wendu":"27"},"status":1000,"desc":"OK"}