部门新来一个架构师,BAT出身,家住三环,开着宝马上班,有车位。图片来自Pexels小伙话不多,但一开口就坚定自信。原因是有上亿的高并发经验,每一秒的请求都是其他跑了一年的公司无法企及的。这就很让人羡慕了,毕竟他靠这个挣的钱比我多。俗话说,要想在公司不出事故,就不要写代码。工作太多容易出事故,谁也顾不得轻松。这就是现实。但有时一切都与结果有关。新的研发负责人不懂技术,但是他懂技术指标,所以统计了大家Git提交的次数。如果Git活动像A股一样绿色,就算是及格了。想来想去,架构师决定取一个并发最高的需求:统计接口的平均响应时间和自启动以来的请求数。为什么说它的并发度高呢?这是因为它统计了所有的接口,自然大于每个接口的请求量。AOP代码封装,每个接口都要从他那里绕过去。又到了我们架构师上场的时候了,代码如下:架构师说我的代码不需要注释。所谓注释,就是针对垃圾代码的。我想是的,他显然受到了Netflix的影响。考虑到高并发场景,程序使用了一个线程安全的ConcurrentHashMap,然后通过每次监听Key来获取对应的数据,然后对Value进行自增。这么简单的代码不需要加任何注释。作为项目中并发度最高的代码,出于对资深架构师的信任,我们不需要做任何代码审查,也不需要做任何测试。大家都很忙,码你,上网走走。我建议你先找代码的问题,如果你找到了问题,你比架构师强;如果你没有找到,并不能证明你比架构师弱,没有什么好难过的。下面插一张图挡思维:安装B被雷击,在线运行一段时间后内存溢出。大家争论不休。毕竟正如我所说,内存溢出问题的排查周期很长,平均需要40天左右才能解决问题。大家开始演示的时候,架构师偷偷启动了EclipseMAT。MAT很适合分析内存问题,但是前提是你需要拨弄栈。架构师可以用Jmap,最重要的是它的权限很大,所以自己做了一份,离线分析。我能理解他的心情。毕竟,定位到自己代码中的问题,并不是什么值得高兴的事情。他发现内存堆里全是MonitorKey和MonitorValue:Monitor$MonitorKey@15aeb7ab我和架构师关系很好,他就问我:我们的接口很多吗?我说:不行,你不管访问量多,这么狗屁的业务能有多少接口?数百个将持续很长时间。他说:我在一堆里发现了好几千万个……说完他就不说话了,因为他发现很多都是一样的界面。肯定是因为参数的问题,所以他在代码中加入了这个,把?后面的部分截掉了。key=key.split("\\?")[0];结果公布在网上,过一会内存又溢出了。这次终于引起了大牛们的注意。经过大家的分析,发现是代码忘记重写MonitorKey的equals和hashCode方法了。我不禁脸红了,作为好朋友,我不应该让他出丑。但是我隐隐感到高兴,因为他的工资比我高。所以这是一个大问题。很多同学对HashMap的知识点都答得很流利,甚至把红黑树也特意背了下来。可当他换种方式问出来的时候,却又是一头雾水。其中一个问题是:一个普通的对象可以作为HashMap的Key吗?答案显然是可以的,但是需要注意重写hashCode和equals方法。如果忘记重写,很大概率会出现内存泄漏。遗憾的是,现实中遗忘的情况很多。Danielarchitects也将被招募。代码重写hashCode和equals方法后,线上没有内存溢出。等等,还没完。毕竟我是架构师,光是这样的bug还是不能证明我的水平的。架构师写的错误一定是不寻常的。这种事情发生的多了,研发领导对技术的权威也不再那么冷淡了。我们决定从并发度最高的代码开始,进行代码审查。不幸的是,架构师的访问代码有问题。虽然问题不是很大,但毕竟是个问题。在统计数据的时候,代码使用了ConcurrentHashMap,但是没有用。在visit方法中,先取出Key,然后判断为空,再插入value。这显然不是原子操作。线程1:获取keya的值线程2:获取keya的值线程1:a为null,生成ab线程2:a为null,生成ac线程1:savea=b线程2:savea=c这时,B丢失了。业务可以忍,严谨的技术专家忍不住,提出修改建议。架构师说在visit方法中加一个Synchronized就可以了。publicsynchronizedvoidvisit(Stringurl,Stringdesc,longtimeCost)我说不行。有一种更优雅的写法,而且效率更高。即使用putIfAbsent方法,代码改动如下:(timeCost);value.avgTime=value.totalTime.get()/value.count.get();大家对这两种方法争论不休。技术总监托着下巴想了半天,看着满脸通红的同学们,说道:这就是我不信任你们的原因。在线环境应尽可能稳定,变化最小。既然加个Synchronized就可以轻松解决问题,为什么不直接用呢?以下代码更改太大且有风险。所长转头对我说:这个bug不一般。为了让大家吸取教训,你会对整个事件做一个回顾。把排错的过程和经验分享给大家,让大家按照这个简单的结构走下去。在日常工作中,我们也要尽量以结果为导向。不管我们用什么手段,只要我们能把事情做好。这就是这篇文章的由来。虚心受教,同时明白自己的薪水不会涨。如果你给我点赞或者友情,或许可以安慰我。作者:小姐姐的狗简介:一个不让程序员走弯路的公众号。专注于基础架构和Linux。十年架构,每天百亿流量,与你探讨高并发世界,给你不一样的滋味。编辑:陶佳龙来源:转载自微信公众号味觉小姐(微信公众号ID:xjjdog)
