避坑 | 早跟你说了不要写 hardcode!
想要成為編程大牛,不僅要具備充足的理論知識,還要有在實際項目中積累的編程經驗和解決問題的能力。
有時,出于經驗不足或者圖省事兒,我們會在開發時寫很多 hardcode。
什么是 hardcode?
hardcode,即硬編碼,就是把未來可能發生變化的信息直接以固定變量的形式寫在代碼中,或者把一段特殊的邏輯寫死。hardcode 會使得日后修改和維護代碼變得困難。
下面列舉幾種常見的 hardcode:
1. 最常見的 hardcode 當屬寫死 ip 地址和數據庫賬號密碼了,比如下面代碼:
// 第三方接口的 IP 地址 private static final IP = 10.10.10.1; void doPost() {// 得到請求地址String requestUrl = IP + '/getUser';// 發送請求... }看似沒什么問題,但是如果要請求的接口地址發生變化,而業務負責人沒有收到通知或者未即時修改,會導致所有的請求失敗!
2. 寫死邏輯,比如在查詢火車票價格時,針對特定 uid 的用戶(比如某大客戶)返回特殊的促銷票價:
// 特殊用戶直接返回票價 if (uid == 10001) { return 50; } else { // 從數據庫查詢票價 return db.getPriceByUid(uid); }
這里其實有兩個 hardcode,"10001" 和 "50"。如果后續該用戶的特殊身份失效,或者票價發生變化,又或者需要再給一批 uid 添加特權,都需要改動代碼,重新上線(在大公司,代碼不可以隨便發布,通常要通過審批等流程),非常麻煩!
以上好像還不能看出 hardcode 的危害有多大,如果項目代碼都在自己的掌控之下,大不了數值變化時改代碼唄。但是在大公司,或者人數眾多的項目團隊中,可能有些 hardcode 沒那么容易被發現,hardcode 帶來的影響和危害可以說是致命的!
下面我用自己悲傷的加班經歷告訴大家,為什么不要寫 hardcode。
一行 hardcode 助我加班
背景
公司內部有一個知名的消息隊列服務,我負責的項目中用到了消息隊列生產端進行消息的推送,供其他業務方訂閱消息進行獨立處理。
一般情況下,連接消息隊列就和連接數據庫一樣,輸入地址、賬號、密碼,直連消息隊列生產者即可,如圖:
但是有時我們不直接連接消息隊列本身,而是連接代理,由代理負責將要發送的消息推送到不同的消息隊列生產端,再進行發送,可以實現負載均衡。此時在代碼中配置的地址是代理的地址而不是某一個具體的消息生產者,如圖:
由于數據量很大,我們使用第二種模式,在代碼中連接生產者代理,但之前代理使用的是固定的 IP 地址,代碼如下:
final String IP = "10.10.10.2"; MessageProducer mp = new MessageProducer(IP, "xx", "xx");固定 IP 毫無疑問是個 hardcode。但打死也沒想到,這段不起眼的小 hardcode,給我帶來了巨大的工作量!
風云突變
前幾天,我正在恰可樂,該消息隊列的服務團隊突然發了個公告,內容如下:
這時,我才想起來那段 hardcode,改吧改吧。。。
下面是一頓操作:
1. 先在代碼中把 IP 地址改為他們指定的域名
2. 用腳本檢測我們線上機器到這個域名的網絡連通性,千萬別 ping 不通!
3. 確認連通性后,部署服務到測試環境,進行消息推送測試
4. 測試通過后,提交代碼,等審批
5. 審批之后,灰度部署(只升級部分機器)
6. 觀察無問題,全量部署
搞定?
深藏不露
如果這樣就搞定了,那真是太謝天謝地了。
改 bug 不可怕,可怕的是,你不知道有沒有 bug,bug 藏在什么地方。。。
原來所有使用這個消息隊列的代碼都配置的固定 IP,而此時我只是更換了自己負責的業務代碼中的 IP 地址,但是我們依賴的其他服務(或者是一個 jar 包),有沒有使用到這個代理,從而引入了 hardcode 呢?必須要全面且仔細地排查!
通過 gradle build --scan 命令掃描項目依賴的 jar 包,排查有沒有這個消息隊列的連接包。
經過排查,還真有一個 jar 包,使用該消息隊列進行監控數據上報。在這個 jar 包中,靜靜地躺著似曾相識的 hardcode:
final String IP = "10.10.10.2"; MessageProducer mp = new MessageProducer(IP, "xx", "xx");于是,我將 IP 切換成域名,然后升級包的版本,在依賴中使用新版本 jar 包。
搞定 x 2?
慈航普渡
此時,只是解決了自己業務中的 hardcode,但是,使用這個包含過期 IP 的 jar 包的項目非常多,如果這些項目不對 jar 包進行升級的話,幾天后用到這個 jar 包的項目都會受到影響!
既然看到了,不能不管吧!
于是,調查了所有可能使用這個 jar 包的團隊,然后給他們發送了升級該 jar 的通知,這才終于搞定了!下班下班。
沒想到,一行 hardcode 就帶來了這么大的風險和工作量,善哉善哉。
既然 hardcode 危害那么大,有什么辦法避免寫 hardcode 呢?
如何避免 hardcode?
針對不同的 hardcode,有不同的處理方式,此處總結如下幾個技巧。
1. 變量引用
最簡單的做法,就是為相同的固定值定義一個變量(常量),最好在單獨的類或文件中。這樣,在修改時,只要修改一處即可。
錯誤代碼:
String requestUrl1 = "10.1.1.1" + "/getUser"; String requestUrl2 = "10.1.1.1" + "/getSku"; String requestUrl3 = "10.1.1.1" + "/getOrder";正確代碼:
如果要調用第三方接口,最好不要在代碼中直接寫死 IP 地址,而是使用域名作為地址調用,可以通過在機器配置 host 來改變實際的服務 IP,便于維護。
2. 配置化
將所有可能改動的信息(比如數據庫賬號、密碼、服務端口)單獨存放到配置文件中進行管理,通過讀取配置來獲取信息。修改信息時只需要獨立修改配置文件,而不用改動代碼,非常方便,是目前項目開發中最常使用的一種方式。
示例配置文件:
server.ip = 10.1.1.0 server.port = 8080 db.username = yupi db.password = yupi讀取配置文件的代碼:
3. 動態配置
通過配置化的方式已經能夠解決項目中的 hardcode,但還有一個問題,就是改動配置后,項目可能需要重新啟動才能讓新配置生效,還不夠靈活。
為此,我們可以利用分布式配置中心實現動態配置,將所有的配置存放在數據庫或分布式緩存、Etcd 中,通過在業務代碼中引入 SDK 來監聽配置變量。當配置發生修改時,變量的值會同步進行修改,而無需重啟項目。
現在比較主流的配置中心有攜程的 Apollo、阿里 Nacos 和 Spring Cloud Config 等,很多配置中心提供了可視化的界面,可以方便地進行配置管理、修改發布和版本控制。
攜程 Apollo 控制臺
以上就是關于為什么不要寫 hardcode 以及如何避免寫 hardcode 的全部內容啦。
如果覺得有幫助,記得分享給小伙伴,再寫硬代碼頭打爛!
有道無術,術可成;有術無道,止于術
歡迎大家關注Java之道公眾號
好文章,我在看??
總結
以上是生活随笔為你收集整理的避坑 | 早跟你说了不要写 hardcode!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 廖雪峰Java1-3流程控制-9brea
- 下一篇: 异想-天开 python---while