依赖——软件工程师的痛

为何各个语言都会有这么多的依赖问题?

软件包的分发规模产生了巨大的变化

大部分主流编程语言都诞生于上个世纪,代码包的分发范围在当时仅限于小规模的团体,例如公司内部或者单个软件项目内部,这种分发规模 只要内部有良好的代码约定就不会致使模块依赖冲突,但今天咱们已经普遍运用github社区来分发软件代码包,分发的规模已经跨越国界跟种族以及不一样的语言,这在当时是没法想象的。python

  • 例如现现在依然很是流行的C语言,就存在经典的函数命名冲突的问题,在小规模团体内部能够经过约定函数命名前缀来避免编译时的冲突。C++在后续引入了命名空间解决了这一问题git

  • C++编译器版本众多,又有不少公司或者团体采用二进制闭源发布代码,又致使了二进制ABI依赖问题, 在今天你依旧能够看到golang swift这些新兴的编译型的语言都是依赖源码编译的,由于它们大多各自语言的各个版本之间的并非二进制兼容的,例如X86 就有fastcall stdcall 等等函数调用约定,以前我翻了一下golang的内部实现,发现go语言在内部又搞了本身一套汇编调用约定,在go使用标准的C函数库的时候还须要作一些转换。github

动态语言在运行时连接函数

动态语言天生就有的毛病,全部的调用都是在运行时才能确认被调用的模块的位置以及代码,由于在动态语言中,并不像C/C++那样在编译期有大量的静态检查,固然你要是喜欢用C/C++的void * ,上帝也无法拯救你。golang

  • Java经过虚拟机字节码兼容性约定以及命名空间,在字节码层面使用操做数栈屏蔽了编译型语言的二进制调用约定问题, 但其动态连接,依旧没有避免运行时依赖缺失的问题,例如包A依赖了包B的v1版本,可是在项目中引入的包C的依赖了包B的v2版本,在maven的仲裁机制下,编译后只导入了包B的v1版本,当程序跑进包C里面的代码就会由于包B的v1版本缺失了一些v2版本的特性而报错,并且这些由于maven仲裁机制致使的依赖缺失的问题并不会在编译期被发现,大多只能等到线上运行的时候才会被发现,若是包C的代码并非热点代码,大部分时候程序并不会跑进包C,颇有可能你会在深夜由于报错日志而被领导催促起床来解决版本依赖问题。解决这个问题的办法只能依靠版本语义管理,在每次发布前都要检测maven版本仲裁是否存在版本号不兼容的状况。

例如 aa.bb.cc 这种版本号,cc表明bugfix的版本,
大多时候出现这种依赖冲突,人工仲裁选择使用高版本便可,而bb表明大的改动,可能存在接口不兼容的状况,
这个时候就要对依赖进行代码检查,确保Java动态连接调用没有问题才能上线使用。
固然不少公司内部的包并不存在版本语义化管理的规范,这个时候你只能祈求上帝,编写你依赖模块的那个老哥,没有修改外部接口编程

  • 在Python里面一样存在相似Java这样的版本冲突致使的依赖缺失问题,并且这些问题大多时候很难被发现,只能等到运行时,例如你在代码中使用模块A的高版本才有的python方法,可是你运行时import了一个低版本的模块A,这个低版本的模块A里面恰好缺失了模块A高版本才有的python方法,而刚好这个代码并非热点代码(在服务器上老半年不必定会跑到的地方),那就等着这颗雷在线上随时爆炸吧。

微服务下彻底失控的依赖管理

在微服务下,以我我的的观察,大部分公司的微服务接口,可能只采用了简单的人肉管理,并且微服务大多都是跨部门维护接口的状况,这个时候若是沟通不顺畅,更容易出现问题。固然跨服务的RPC调用大多都是热点代码,若是出现问题,大多很容易被暴露出来。swift

  • Java中使用了feign 经过静态编译约束大几率上解决了 接口参数的问题, 但若是你的feign模块是依赖方的低版本,而对方在生产发布流程中部署了高版本的服务,照旧会出现问题,固然这个时候须要强有力的跨部门跨技术组的协调方案,否则依旧会暴露依赖冲突这个问题。
相关文章
相关标签/搜索