这是实际存在的一个需求。
天气请求是一类按次数付费的查询接口。在实际应用中,请求量非常大(保守估计数k到数十k次/天),如果不做缓存处理,那么等着被老板拉去祭天吧。
假如我有一个city.json文件存放城市代码库:
{
"城市代码": [
{
"省": "北京",
"市": [
{
"市名": "北京",
"编码": "101010100"
},
.....
通过开放的api拿到对应城市的天气数据。然后存放到一个weather.json中,应当怎么做?
本文将实现该业务的全部代码。
这个需求就用原生node来写吧。
首先,先安装request库,并封装一系列工具方法
// 读取文件
const read = (path) => {
return new Promise((resolve, reject) => {
fs.readFile(path, (err, data) => {
if (err) {
reject(err);
} else {
try {
data = JSON.parse(data);
resolve(data);
} catch (_err) {
reject(_err);
}
}
})
})
}
因为是使用json文件作为持久化储存,因此本文采用fs模块作为写入读取和调用方案。
根据ip获取城市有免费稳定的服务,这里用的是阿里提供的接口。
自己测试时这个接口也时常神经,考虑时常换着请求。实际生产中不会存在这种问题。
// 根据ip获取城市
const ip2city = (ip) => {
return new Promise((resolve, reject) => {
const url = `http://ip.aliyun.com/service/getIpInfo2.php?ip=${ip}`;
request({
url,
json: true
}, (err, res, body) => {
if(!err){
resolve(body);
}else{
reject(err)
}
})
})
}
这里涉及到了在本地city.json中获取城市代码。
相关的json文件是我在博客园找的。
https://www.cnblogs.com/longyi/p/3387320.html
// 根据城市获取城市代码
const getCityCode = (province, city) => {
return read('./city.json').then((data) => {
data = data["城市代码"];
let cityCode = '';
data.map(prov => {
if (prov['省'] == province) {
prov['市'].map(cit => {
if (cit['市名'] == city) {
cityCode = cit['编码']
}
})
}
})
return cityCode
})
}
这里用的是sojson,没什么说的
// 根据城市获取天气
const city2weather = (cityCode) => {
return new Promise((resolve, reject) => {
let url = `http://t.weather.sojson.com/api/weather/city/${cityCode}`
request({
url,
json: true
}, (_err, res, body) => {
resolve(body)
})
})
}
也就是从weather.json中拉出数据,判断是否存在。
// 由城市代码获取天气
const getCityWeather=(cityCode)=>{
return read('weather.json').then(weatherData=>{
console.log(weatherData)
let isExist=weatherData.filter(x=>{
return x.cityCode==cityCode;
})
if(isExist.length==0){
console.log('不在本地,需要调用线上接口..')
// 发起请求
return city2weather(cityCode).then((body) => {
if (body.status == '200') {
let content={cityCode,body};
weatherData.push(content)
fs.writeFile('./weather.json', JSON.stringify(weatherData), err => {
console.log('写入成功');
})
}else{
console.log('请求错误')
}
return body;
})
}else{
console.log('请求的数据已在本地,将调用本地数据...')
return isExist[0];
}
})
}
就是串联上述逻辑。同样是没什么可说的
// let ip='27.17.222.255'
// let data='14.124.192.0'
// let ip='223.104.63.110'
let ip='14.124.192.0';
ip2city(ip).then((data)=>{
// console.log(data.data);
let province=data.data.region
let city=data.data.city;
return getCityCode(province,city).then(cityCode=>{
console.log(`您查询的城市为:${province}省 ${city}市`);
// 需判断是否在ip列表里
return getCityWeather(cityCode).then((msg)=>{
console.log(msg);
})
})
}).catch(err=>{
console.log(`请求城市错误:${err}`)
})
第一次请求:
第二次请求
那么实际业务就已经实现了。
这种方案最后造成的weather.json文件会非常大(约1M多),实际上基本可以不担心。实际上这里的json最终还是需要从数据库层面来实现。操作数据库效率显然更高效稳定。
当然可以其它优化的地方很多。比如在sojson的接口是8小时更新一次,你可以继续写入请求时间戳是否过期,过期则走重新请求。