原生node根据ip地址查询天气及其缓存方案

这是实际存在的一个需求。

天气请求是一类按次数付费的查询接口。在实际应用中,请求量非常大(保守估计数k到数十k次/天),如果不做缓存处理,那么等着被老板拉去祭天吧。

  • 获取请求ip,判断是在中国哪个城市(省)。
  • 根据本地的city.json查询出城市代码
  • 判断城市代码在不在城市列表(weather.json)里。
  • 如果不在,把ip写入城市列表,
  • 是否过期?过期则重新请求,更新城市列表。未过期则从json文件中取出对应的天气,发送出去。

假如我有一个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获取城市有免费稳定的服务,这里用的是阿里提供的接口。

自己测试时这个接口也时常神经,考虑时常换着请求。实际生产中不会存在这种问题。

// 根据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小时更新一次,你可以继续写入请求时间戳是否过期,过期则走重新请求。

原文发布于微信公众号 - 一Li小麦(gh_c88159ec1309)

原文发表时间:2019-06-09

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券