zoukankan      html  css  js  c++  java
  • 最近遇到的一些坑坑

    1. 未初始化

        因为php语言是弱类型语言,不需要声明变量类型,也不需要初始化值,所以经常对变量直接用,也不初始化。在php中这种做法不会造成致命错误,但是不初始化的这种思路会出问题。项目中经常要写一些脚本来统计业务的增长。增量的计算是今天基于前一天的增长,如果前一天的总量数据计算失败,如何计算增量呢。如果这个程序在一台新的机器上运算,没有前一天的数据,如何基于前一天的数据计算今天的增量呢。所以在计算增量的时候,两天数据的初始化是重要的。
    除此之外,有的业务讲究原子性,执行业务B必须在业务A执行完毕之后。但是如果业务A一直执行失败,B就一直等待,然后阻塞后面的C业务,D业务。比如说下了订单之后,需要支付成功才能发货。那么如果支付一直不成功,这种crash如何捕捉又如何自动恢复。有一个同事写的脚本就是业务A执行失败的时候没有报警,而业务B一直在等待业务A执行成功的状态,没有尝试过重试,没有去自动恢复。导致后续的业务都阻塞了没有执行。
     
    上面问题的关键,需要初始化程序状态,不要只想着程序在正常预期的状态,应该考虑异常的状态,如何捕捉异常状态并且自动恢复。
    问题一,我们当发现前一天的数据不存在的话,就重新初始化程序状态。或者获取到最后一次成功的数据状态作为原始的数据。
    问题二,当业务B捕捉到业务A未在预期时间内未执行成功的时候,发出报警,并且自动重启重试。  
     
    2. 添加一条日志引发的蝴蝶效应
        本来以为在程序中添加一条日志,没什么关系,不就加一条日志,对业务逻辑又没什么影响。后来才知道自己是too young,too simple。因为这个success日志是在业务处理成功的时候打。业务量每天都可以达到10亿,所以一下子每天多出10亿日志。
    (1)首先日志量过多,而磁盘有限。所以磁盘保存的日志天数减少,之前可以保存5天,后续只能保存3天。你也许会说,日志少了就少了呗,又不影响线上业务。但是对于技术运营来说,亲你知道日志对于排查问题的重要性么,就相当于破案之保留现场的重要性。
    (2)再者是日志量过多,导致日志同步过慢,每10分钟的日志10分钟都无法同步完。比如在19:00的时候将2015/12/18/19/00将日志移动到backup_log文件夹,然后进行rsync,rsync时间过长,超过了10分钟,在19:10的时候2015/12/18/19/10日志已经移动到了backup_log文件夹。原有的逻辑是rsync完之后,要清空backup_log文件夹。so第二个十分钟2015/12/18/19/10的日志还没rsync就被清空了,导致同步日志丢失。后续考虑不删除backup_log的文件夹。日志都是按照时间分片的,虽然rsync是按照增量推送,因为推送整个backup_log文件夹,导致一下子文件夹中包含了很多的分支(19点的6个分支,18点的所有分支。。。),从而导致rsync的连接数过多。最后决定指定文件夹推送,不推送backup_log整个文件夹,推送19/00的日志,就指定推送00的日志文件夹,推送19/10的日志,就指定推送10的日志文件夹。
    (3)后续就是日志分析,因为每次都是10分钟日志全局搜索,由于日志量太多,写入内存也变多,搜索也变慢,日志分析也慢。
    真是动一发迁全身,一步错步步错。后来将日志进行精简,解决了上述各种问题。
     
    3. for循环,if等各个分支的考虑
         很多人在写程序的时候,估计也犯过这样的错误,在for循环中有if判断,if判断只考虑某些情况,不考虑for循环或者while循环的各个分支。特别是在for循环和while循环要剔除某些情况,经常不用continue而是直接return false。但有时候也犯相反的错误。某些异常不满足执行条件,不能执行for循环以后的逻辑,所以要直接return false,却使用了continue,导致不符合要求的分支在for循环之后继续往下执行。
        这个错误虽然很低级,但是大家经常犯。所以在代码review的时候,一定要含情脉脉多加凝视,多加考虑再三。否则后续出问题,排查起来问题,坑坑可不会轻易饶过你。嘿嘿~
     
    4. linux命令的滥用(awk/sed/sort/rsync)
         由于linux命令的简短,很多人喜欢在程序中使用shell命令,简短的前提有时候是以牺牲可读性为前提的。一旦程序的可读性得不到保证,就容易滋生bug。前段时间日志分析有一个地方需要对日志按照某一个字段进行拆分。比如说要按照第5个字段$5进行拆分。大家就喜欢使用 awk '{print $0 > $5}'; 这个代码确实非常的简洁,但是在处理一个大文件的时候,效率特别慢,特别是在awk还有pattern匹配判断的时候。慢的原因是awk只是单进程工作,而且需要锁住整个文件进行处理。曾经有考虑将文件split,然后拆分速度有所提高。但是终究不是解决根本问题。最后决定采用多进程读取本地文件的方式进行拆分到各个子文件,程序可读性提高,效率也提高。同时采用定时实时处理的模式,每一个小时拆分前一小时的日志,这样极大的快速完成任务。而且使用php可以实现比awk更加复杂的逻辑。之前程序rsync的时候因为rsync是增量推送,所以每小时都将日志处理结果的整个文件夹rsync过去。但是rsync还是很慢,因为rsync要比较,如何让rsync不比较,同事查手册,加了一个--append的选项,就能直接将数据附加在后面,而不需要对数据进行比较。这是一种方法提高rsync,其实我们也可以只推送新产生的数据,就是对推送的日志打上时间戳,将这个日志推送过去。
    这件事也让我明白:
    (1)业务程序的可读性摆在第一位
    如果程序不可读,一是容易逻辑混乱,滋生bug。二是后续接手的人不好维护,无法清晰理解程序逻辑,从而引进更多bug。
    (2)每种程序语言或者工具都有其使用条件和局限性
    awk固然简短好用,但是在实现多进程处理和复杂业务逻辑处理的时候还是有其局限性,毕竟它只是一个工具语言,而非一个适用于实现复杂业务逻辑的高级语言。为什么会误用awk和rsync,关键还是对工具了解不深,从而无法了解工具的适用场景和使用局限。
    (3)尽可能简单,少冗余(Keep it simple)
    之前推送整个文件夹,完全可以按照小时推送新增加的数据。不需要推送整个文件夹,导致不必要的比较。在优化rsync的时候,添加--append的配置参数,和按照小时推送都是一种优化方式。什么是简单,多一个不多,少一个不行。
     
     
     
  • 相关阅读:
    Java实现 蓝桥杯VIP 算法训练 校门外的树
    Java实现 蓝桥杯VIP 算法训练 统计单词个数
    Java实现 蓝桥杯VIP 算法训练 统计单词个数
    Java实现 蓝桥杯VIP 算法训练 开心的金明
    Java实现 蓝桥杯VIP 算法训练 开心的金明
    Java实现 蓝桥杯 算法训练 纪念品分组
    Java实现 蓝桥杯 算法训练 纪念品分组
    Java实现 蓝桥杯VIP 算法训练 校门外的树
    Java实现 蓝桥杯VIP 算法训练 统计单词个数
    Java实现 蓝桥杯VIP 算法训练 开心的金明
  • 原文地址:https://www.cnblogs.com/TsingLo/p/5030788.html
Copyright © 2011-2022 走看看