• 开发函数计算的正确姿势 —— 排查超时问题


    写不尽的 code,查不完的 bug

    通常我们写 bug,哦,不对,写代码时总不会一帆风顺,往往各种 bug 充斥其中,即使测试有较高的代码覆盖率往往也会有漏网之鱼。能写出一些比较隐蔽或者看起来像 feature 的 bug,并且经过了测试、code review 等层层的考验,最终 merge 到主干,这也算的上是一种本事。

    这次,我们讨论的场景是,当你制造的 bug 被别人发现或者不小心把自己坑了,而不得不去 fix,且你自己也忘了这个 bug 是怎么写的了,在这种情况下,如何排查问题?

    一气呵成,bug 侧漏

    var request = require('request');
    
    exports.handler = function(event, context, callback) {
        console.log("event: " + event);
        console.log('context: ', JSON.stringify(context));
    
        const options = {
            url: 'https://saweather.market.alicloudapi.com/spot-to-weather?area=%E6%B3%B0%E5%B1%B1&need3HourForcast=0&needAlarm=0&needHourData=0&needIndex=0&needMoreDay=0',
    
            headers: {
                Authorization: 'APPCODE 5d9129e294fc4f518793ae9f9a15dbff'
            }
        }
    
        request(options, function (error, response, body) {
            if (error || response.statusCode != 200) {
                console.log("error " + error);
                return 
            } 
    
            console.log(body.day_weether);
        });
    };

    很简单的一个 nodejs 函数,刚刚接触函数计算往往会意气风发的一口气抒写出这样一段代码,通常是为了简单测试一下函数的使用流程。但是发布到上去后,却出现了调用超时的问题。

    大胆猜测,小心求证

    首先,在无从下手的情况下,你大胆猜测如下:

    1. 函数入口写错了
    2. 代码中的逻辑有问题
    3. 函数计算服务有问题

    为了排除其中的 3 选项,首先通过 fun local,在本地运行测试:

    fun local invoke nodejs_timeout

    得到结果:

    可以看到,程序也被卡在这里了。由此,可以排除掉 3 的可能性。

    接着,可以通过加一些日志,或者单步调试来进一步缩小排查的范围,这里选用单步调试,因为这种方法往往更简单。

    首先,利用 VSCode 在侧边栏下一个断点:

    然后使用以下命令将函数以调试的方式运行起来(调试基本用法 参考,这里不再过多阐述):

    fun local invoke -d 3000 nodejs_timeout

    然后单击 VSCode 的开始调试按钮进行调试:

    可以看到,函数被正确的调用了,且进入到了入口函数。到此为止,可以排除掉 1 的可能性。

    接下来,就要确认 2 可能性中存在的问题。

    在 request 处的代码设置断点,continue 到里面,然后在 Local 可以看到运行到此处时的变量的值。

    可以看到,http 请求返回的 resposne 的 statusCode 为 200,符合预期。body 也是有数据的,这个也符合预期。

    把 body.weakday 放到 watch 里面执行一下,看下结果。

    这个就不是预期中的值了。

    仔细看下 body 这个对象,发现其显示格式不对,通过 typeof 将 body 的类型打印出来:

    这货竟然是个 string

    刚想破口大骂,但一想这代码是自己写的,赶紧闭嘴。

    那就转成 json 吧。

    但转完后,发现事情更不对了,day_weether 并不直接在 body 下,而是隐藏的很深。

    而且名字不叫 day_weether,这里有个拼写错误。正确的写法应该是:

    JSON.parse(body).showapi_res_body.f1.day_weather

    What the.... 脏话都到嘴边了,但一想代码自己写的,总不能骂自己吧,赶紧修吧。

    把正确的表达式贴到 Watch 里面,查看,果然能够取到正确的值:

    验证完毕后,将正确的值粘贴到代码中,心想,总算解决了。

    重新运行一下函数,发现果然能够取到数据了:

    但函数依旧卡在这里,并没有继续往下走————超时的问题依旧存在!

    现在,愤怒的心情已经没有了,取而代之的是蓝瘦,香菇。站起身,望望窗外,让冷风肆虐你的脸庞。许久,心里平静一些了。

    山穷水尽,柳暗花明

    冷静下来,理一理线索:

    从刚才的调试结果来看,函数已经运行,且获得了正确的结果,但是函数却没有结束,直到超时。突然,一个隐约的答案在你身边徘徊,你拼命想要抓住,来回踱步,蓦地,像是出现了救命稻草一般,打开了函数计算 Nodejs 的 文档,你用尽力气摆动眼球,快速阅读文档,并在心里恳求那根救命稻草的出现。恍然间,你如同穿越了一道厚重的铁门,身边的光线突然由暗变亮,你被这明亮的光线刺的睁不开眼睛。但你知道,答案就在这刺眼的光芒里。眼泪,没能止住,顺着你的脸庞缓慢地流了下来。终于,你松开了紧握的拳头,弯下了一直挺直的腰板,眼泪鼻涕突然倾泻而出,你————失声痛哭。所有的委屈从内心经过喉咙、鼻子、眼睛发泄出来,伴随着这一阵阵渐渐衰弱的回荡声,远去。

    哭罢,擦干眼泪,平复下自己的心情,将那一段你追寻许久的答案缓慢但却有力的敲击出来:

    callback(null, JSON.parse(body).showapi_res_body.f1.day_weather);

    终于,这段代码就如同你家叛逆的孩子,脱去叛逆的伪装,显现出它乖巧的样子:

    欲善其事,先利其器

    经过这一次事件,你总结出三条经验:

    1. 写代码要耐心、细心,每一段代码都要经过思考。一口气将功能写完,往往是看上去写的快,但实际上会埋很多坑,坑到别人还好,往往不小心会把自己坑了。
    2. 要多读文档。使用语言,要读语言文档,使用第三方库,要读库的文档,使用产品,要读产品文档。如果不仔细阅读仅凭自己的猜测去写,写出的代码往往漏洞百出。男人千万不要硬撑,别对自己这么很。累了要休息,可以读读文档放松下。俗话说得好,磨刀不误砍柴工!
    3. Fun 工具的熟练使用重中之重。 在前两点都没做到的情况下,却能最终将问题排查出来,Fun 工具功不可没!但与之功劳成反比的是,对 Fun 工具的学习尤其是本地调试功能,花的时间反而是最少的,如此巨大的收益比,使自己下定决心,一定要再花些时间,把 Fun 工具 吃透!

    总结完,你又用心的看着自己写下的代码,就如同看向自己的孩子,宠溺、疼爱、气愤各种心情夹杂在一起。突然,你一皱眉,呵斥道:这样不对,异常的时候,也要返回!

    于是,你纠正了代码的错误行为:

    if (error || response.statusCode != 200) {
        console.log("error " + error);
        callback(error, null) ;
    } 

    就如同,训斥完与人打架的孩子,看到孩子听话了,你露出了欣慰的微笑。

     

    阅读原文

    更多技术干货 请关注阿里云云栖社区微信号 :yunqiinsight

  • 相关阅读:
    在Centos 7下编译openwrt+njit-client
    开博随笔
    Chapter 6. Statements
    Chapter 4. Arrays and Pointers
    Chapter 3. Library Types
    Chapter 2.  Variables and Basic Types
    关于stm32不常用的中断,如何添加, 比如timer10 timer11等
    keil 报错 expected an identifier
    案例分析 串口的地不要接到电源上 会烧掉
    案例分析 CAN OPEN 调试记录 进度
  • 原文地址:https://www.cnblogs.com/zhaowei121/p/10114315.html
Copyright © 2020-2023  润新知