作者:我是小钞票 | 来源:互联网 | 2023-10-11 17:21
问题描述:通过WebServer将监控数据入库到Hbase,在入库之前需要将指标与ip的列表更新到缓存中,以便前台页面随时选择查看。前两天上了一些新用户导致负载增加,逐渐发现某些用户的监控场景出现
问题描述:
通过WebServer将监控数据入库到Hbase,在入库之前需要将指标与ip的列表更新到缓存中,以便前台页面随时选择查看。前两天上了一些新用户导致负载增加,逐渐发现某些用户的监控场景出现丢数据的情况,估计Tps要在1w以上。丢数据会导致前段曲线毛刺增加,体验极差,所以优化WebServer的接收程序需要立马执行。
解决过程:
查看GC,YGC频繁,但FGC基本很少发生,所以丢数据应该不是GC停顿导致的。既然是加了量导致,那么应该跟处理的性能有关。在看一遍代码,看出了问题。首先写入模块实现的简单粗暴,当时为了避免多线同步问题,连接-写入-关闭实现在了一个方法体内。另外此时发现CPU也非常的高,这个之前并没有在意(之前是加入了更新缓存的功能,但是CPU升高并没有引起在意),当时以为是量增大导致,于是先利用nginx加了机器,发现问题并没有解决。重新了入库代码,单例同步方式将数据添加到Buffer,然后达到一定的阈值刷写到Hbase。再一看,还没解决,但是YGC明显减少。CPU还是很高,打开日志查看还有一些因为并发修改导致的异常,定位问题所在,就是更新缓存的模块出了问题。一看代码,确实有几个HashMap没有加同步,只要一遇到异常,那么serverlet线程就退出本次操作,所以后续的消息入库也就无从谈起。之前的量级不大,并且不是时刻调用,所以,由于并发异常导致的崩溃也不是时常发生。再看代码,代码写得很粗暴,由于是每时每刻都有可能会出现新的数据,所以需要用对缓存的数据做判断要不要更新。其实这是多此一举,add方法是密等操作,所以你直接add就行,这样省去很多无谓的比较,判断。而且每次来数据都更新是完全没必要的,因为新数据(主要是一些新添的监控维度和ip)到来的概率不高,并且实时性没必要做到完全实时,所以采用同步add到本地HashMap,然后达到一定阈值在set到memcached之中,这样大大减少无谓的比较已经对缓存的操作。另外在清理过期数据时,可以一天清理一次,判断是否是下一天的开始,如果是下一天,那么就在add中先做一下清理操作,然后在add。
总结:
对于无状态转发,nginx是无二之选;
近实时的应用出问题要看是否出现FGC停顿,有时候是要命的;(当然这次不是因为这个引起)
多线程一定考虑同步问题;尽量不使用多线程;
事先做好批量设计,要清楚各类操作的时耗比例,比如建立连接要比一次写入耗时的多;
权衡实时性与吞吐率,一般而言要考虑吞吐率,在可以忍受的范围稍微降低一下服务质量,性能会有质的飞跃;
幂等操作会省去你很多麻烦的逻辑,也容易提高性能;
这次犯错误下次就不要再犯,否则对不起你逝去的时间;