依賴——軟件工程師的痛

爲何各個語言都會有這麼多的依賴問題?

軟件包的分發規模產生了巨大的變化

大部分主流編程語言都誕生於上個世紀,代碼包的分發範圍在當時僅限於小規模的團體,例如公司內部或者單個軟件項目內部,這種分發規模 只要內部有良好的代碼約定就不會致使模塊依賴衝突,但今天咱們已經普遍運用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模塊是依賴方的低版本,而對方在生產發佈流程中部署了高版本的服務,照舊會出現問題,固然這個時候須要強有力的跨部門跨技術組的協調方案,否則依舊會暴露依賴衝突這個問題。
相關文章
相關標籤/搜索