当前位置: 首页 > 科技观察

源码面前,没有秘密

时间:2023-03-18 19:19:31 科技观察

著名IT作家、翻译家侯杰先生曾在他的作品中说过,也就是我们今天文章的标题《源码面前,没有秘密》”。可以说是相当精致了。我们可以从源代码中收获的内容的高级概述。在源码中,无论是应用的调用逻辑、关系,还是各种设计,都一目了然。为什么会突然想到这样的话题?因为最近上线了一个项目,几个功能模块使用了Redis做数据缓存,包括权限信息。所以每个请求都会经过Redis的认证。这个功能是我指导一位小同学开发的。上线几天后出现这个问题:现象是网上有很多报错CannotgetJedisconnection;nestedexceptionisredis.clients.jedis.exceptions.JedisException:Couldnotgetaresourcefromthepool项目大约等于小流量,Redis的最大数量上线连接数已达到上限1000,用户数远远小于***连接数。Redis使用的是公司的集群,稳定性有保证。理论上,这种获取不到资源的情况是不应该发生的。小同学google了半天也没找到具体问题原因。我提供的几个想法并没有从根本上解决问题。好在调整配置可以离线稳定复现。于是就开始在同学的电脑上调试跟进源码。类似于结对编程,我一边调试,一边给同学讲Redis连接池的实现,目前的疑惑等等??。小同学说刚才是想按照源码来的,但是跟了两层之后,感觉越调用越深,有点头晕,就放弃了。然后继续调试说说原理,资源用完后,在finally释放回收的地方,发现有一段代码判断当前dataSource是否为空,从而执行两种不同的操作。如果不为空,则将资源放回池中,下次继续使用。如果为空,则执行真正的关闭操作,直接关闭Socket。而这两个if/else的逻辑都封装在了一个方法中,如果不跟进的话,是不会发现处理上有什么区别的。我们目前正在使用连接池。预计按照连接池的思路,使用过的需要释放回池中才能继续下一次使用。现在的现象比较奇怪。往前看,我发现虽然Connection整体上使用了JedisConnection,但是在我们遇到问题的地方,它返回了一个JedisWrapper的子类。这是本系学生开发的SpringBootstarter。部分逻辑被重写,connection对象被用作原始对象持有我们不使用的属性。上面判断dataSource是否为空,就是判断JedisConnection中的属性是否为空。Wrapper之后保存origin对象,返回一个新的Wrapper,所以dataSource没有初始化,出现之前dataSource为空的问题。.跟着这里,发现maven的依赖不一样,又切换回标准的SpringBootstarter,问题解决。小学生叹了口气:“这不是犯罪吗?要不是你帮我,我都不知道从何分析起。”我鼓励小同学们在没有想法的时候从源码中寻找。遵循代码的过程也是一个学习的过程。比如这次,跟着你就能明白连接池是怎么实现的了。源码有什么问题是无处遁形的,这些源码也可能存在bug。回过头来看,我们不难发现,也许并不是所有的问题都需要跟着源码走,但是阅读源码,带着问题本身去调试,就像放大了一样,虽然费时费力,但会有所收获很多。一招可以克敌制胜。这也是开源的好处。可以通过源码来分析作者的思路。出现问题时,可以逐行分析,了解细节。这些都是C/C++中的dll文件所没有的优点。:-)对于新同学来说,可能会害怕看源码,觉得这一层嵌套调用有点乱。不过这些都是表象,多试几次就习惯了。老司机为什么叫老司机?一是车技好,二是熟悉车况。这些也是练出来的。因此,无论是作为解决问题的最佳手段,还是学习设计和思考的宝库,源码都是最佳选择。有空就多看看书,短时间你也会是老司机。***,总结一下,在出现一般性问题的时候,如果源码出现异常,可以使用IDE的异常断点,这样在异常发生的时候,会得到一个新鲜的异常栈,完整的调用链就在你面前。一步步深入,先分块找到疑似目标方法,结合前面的步骤,跟进具体的方法,不要根据显示的方法名和参数猜测方法的意图,进去看看如果有必要,万一有隐藏的逻辑?类,查看包名和类名,加载的类是否有误【本文为专栏作家“侯树成”原创稿件,转载请通过作者微信获取授权公众号“Tomcat那些事”】点这里,查看这个作者的更多好文章