- 本章难度:★★☆☆☆
- 本章重点:以场景故事的形式讲述小菜到公司几天后,接到第一个统计调用商品详情接口次数的任务,在梳理了业务流程后,快速实现了功能,但测试结果与预期差距很大,排查和调试了一天也没定位到问题的过程。
大家好,我是冰河~~
“这特么到底是哪里出了问题,我感觉没啥问题啊,为什么我统计出来的数据就是和运维统计出来的数据对不上呢?为啥运维统计出来的结果是正确的呢?我觉得自己的代码没毛病啊?”——此时的小菜已经纠结好久了,几乎到精神崩溃的边缘。
一、需求背景
事情是这样的,小菜是一名刚从学校毕业的大学生,几乎没啥工作经验,凭借着在学校的传奇经历,顺利进入了某头部互联网大厂实习,刚来没几天,就被分到了一个统计线上调用商品详情接口次数的任务。说起来不就是统计一个接口的访问次数吗?这个需求小菜很懂,也很明白,没一会儿就搞定了,可是发布到测试环境测试时,却发生了各种诡异的问题。
二、接手任务
时间过的真快,小菜不只不觉来公司实习快一周了,这几天基本都是学习公司技术资料和项目业务,平时自己写写demo,还没有真正写项目功能。
这不,这天小菜刚到公司,把自己厚重的电脑包往办公位一放,就看到产品经理屁颠屁颠的走过来了,不过不是找小菜的,而是径直走到了小菜的直属领导——老王的身边。
“王工,咱的社区电商项目不是刚上线吗?现在运营有个需求,要统计下访问调用商品详情接口的次数。”
“好,什么时候需要。”
“下周发布上线就行。”
“好的。”
老王把调用商品详情的接口梳理了下,这个需求确实比较简单。老王考虑到小菜来公司好几天了,学习了几天公司的技术资料和项目业务。心里就想着把这个简单的需求,交给小菜做。
“小菜,你过来下,给你个简单的任务”,老王说道。
于是小菜起身来到老王的身边,老王开始巴拉巴拉的为小菜讲解任务需求和对应的接口情况。
虽说小菜没啥工作经验吧,但是这个需求听起来确实比较简单,小菜听完老王的讲解后,说道:“没问题,我尽快完成”。
于是小菜回到工位,开始认真分析代码,并在本子上画实现的流程。
三、梳理任务
虽说小菜没啥工作经验吧,但是他确实是懂得在搞清楚任务需求和实现流程之前,不会轻易干代码的。这也是他刚来公司的时候,老王跟他说的,说起老王,特么确实是个大好人,新人刚来公司的第一天,就会将自己的一些踩坑经验巴拉巴拉的分享给这些新人(我刚参加工作那会儿怎么就遇不见像老王这样的大好人呢?)。
经过认真的思考和仔细的梳理商品详情接口后,小菜画出了下面的这幅图。
图片
客户端在访问系统接口时,首先会经过网关,由网关将访问系统的流量路由到后端微服务。在网关的设计和实现上,总体上会分为网关核心组件和网关控制台,网关的一些规则,比如接口统计、UV统计、PV统计,鉴权规则,其他规则等等,都是在网关控制台进行配置,并且在网关控制台的配置会及时生效。
网关采用责任链设计模式实现了一系列的拦截器链,比如风控拦截器、接口拦截器、鉴权拦截器、其他拦截器等,每个拦截器专注实现某种特定逻辑的校验规则,例如风控拦截器会调用风控系统检测请求是否存在风险,接口拦截器主要是统计接口层面的一些请求信息,鉴权拦截器主要是检测与鉴权相关的逻辑等等。到达网关的请求只有通过所有拦截器的校验后,才会被路由到后端服务。
梳理完请求的流程和网关的拦截器逻辑后,小菜拿着本子走到了老王的面前。
“老大,业务流程我梳理清楚了,你看看对吗?”
“好,我看看”。
大神就是大神,只见老王接过小菜的本子后,只是看了一眼,说道:“可以这样实现,没问题,实现的过程中遇到自己解决不了的问题,可以再问我。”
“好的”。
小菜回到了工位上。
四、实现任务
要不说这个任务很简单呢?小菜梳理清楚业务流程,向老王确认可以这样实现后,回到工位,啪啦啪啦就翘起了键盘,没一会就完成了代码开发。
“这个功能确实是简单啊,我也只是花了没多少时间就完成了,看来公司的项目其实也挺简单的,哈哈哈”——小菜心里暗暗自喜。于是乎,小菜并没有在自己本地对写完的代码进行单元测试,他觉得这个功能太简单了,没必要测试。所以,小菜将代码合并到了测试分支,由 CI/CD 平台自动构建并发布到了测试环境。
此时的小菜向测试提交了一份文档,详细的描述了自己这次实现的业务功能,交付测试。不一会儿,测试便将测试结果反馈给了小菜。
小菜拿到结果一看,瞬间懵逼了:“卧槽,不是吧,期望值10000,实际值7596?差距这么大吗?不可能吧?这功能很简单啊!就是计数啊!是不是测试搞错了?(应该大部分程序员首先会觉得是别人的问题吧,哈哈哈哈)”。
于是乎,小菜重新打开开发环境,一遍遍排查自己写的代码,也在自己本地一遍遍调试着自己的代码。
过了很久,小菜得出一个结论:没毛病啊,结果是对的啊!于是小菜去问测试:”你是怎么测试的呢?“。
“我就是按照正常流程测试的啊,你写的代码肯定有问题”。
“没问题啊,我自己调试半天了,结果是对的”。
于是,测试给小菜发了一份运维从测试服务器上统计的结果数据,敲好与测试的结果一致。
小菜看到数据后,说了句:“好吧,我再看看吧”。
小菜回到工位,又开始了排查代码和调试代码,
就这样,小菜从上午一直排查、调试到快下班了,得出的结论是:没毛病啊,哪里除了问题呢?
此时的小菜已经失去了刚做完这个功能时的自豪感,心情也越来越烦躁。“到底哪里有问题啊?我看代码没毛病啊!怎么就不对呢?”
五、求助老王
实在是没招了,小菜起身走到老王身边:“老大,上午写的那个功能,发布到测试环境,测试说我统计的结果数据不对,运维那边从服务器上统计的结果是正确的,我排查了很久都没发现问题。”
“是吗?我看看你写的代码。”
于是老王将测试分支的代码拉取到本地,找到小菜的提交记录,仅仅看了一眼代码,就发现了问题所在。
原来在小菜写的代码里定义了一个接口访问计数器。
源码详见:concurrent-design-patterns-immutable工程下的io.binghe.concurrent.design.wrong.WrongCounter。
public class WrongCounter {
private int visitCount;
public void accessVisit(){
visitCount++;
}
public int getVisitCount() {
return visitCount;
}
}
这个类中定义了一个int类型的成员变量visitCount,用来统计接口的访问次数,每次访问接口时,在网关的接口拦截器里都会调用一次accessVisit()方法,将visitCount的值加1,这样不断累计接口的访问次数。表面上看起来逻辑是没毛病的。
老王对小菜说:“这个类实现的有问题,这样实现根本就不能统计出正确的结果”。
“不对呀,我排查和调试半天了,没问题呀,结果跟我自己预想的一样呀”,小菜确实不知道哪里有问题,也并没有发现这个类的不妥之处。
正好此时到了下班的点,老王今天还要早点回去接孩子放学,于是就对小菜说:“这样吧,你先回去想想,查查资料看看哪里有问题,我今天要早点回去接孩子放学,明天到公司了,我给你讲讲哪里有问题”。
“好的”,小菜回答到。
就这样,看起来一个很简单的问题,小菜前前后后花了一天的时间,也没搞定,心里确实有点郁闷:“明明是很简单的功能啊,这特么到底是哪里不对呢?”。
他又回到了座位上。。。
六、本章总结
本章,主要以场景故事线的方式讲述了小菜来公司几天后,接到第一个统计访问商品详情接口的项目任务,本以为很简单的功能,三下五除二做完,提交测试环境后,被测试打回,反复排查和调试代码,也没发现问题的过程。此时的小菜心里有点郁闷,明明很简单的功能,这特么到底是哪里不对?
好在明天到公司了,老王会给小菜讲清楚到底是哪里的问题(再次感叹:老王是真特么的好啊!)
最后,可以在评论区写下你学完本章节的收获,祝大家都能学有所成,我们一起搞定高并发设计模式。