世上本沒有坑,踩的人多了也便成了坑。每遇到一次困難,每踩一個坑,對程序員來講都是一筆財富。持續學習是程序員保持競爭力的源泉。本期將分享一個踩坑無數的Java程序猿填坑祕籍。前端
榆木,一個閱歷無數(踩坑)的技術宅男,喜歡瞭解新技術卻不愛太鑽研新技術(由於懶,猿屆反面角色一枚)。14年畢業至今,在Java開發這條道路上可謂是坑過好些人、也埋過好些坑、也被坑過好些次。由於懶,沒有針對他遇到過的問題作過太多的筆記(記錄一些棘手問題的解決方法仍是個不錯的習慣),只是習慣性的去分析爲何出現這樣的問題,咱們該怎麼去避免重複出現。在這裏榆木share一下第一次作獨立需求的過程。mysql
榆木·Java開發工程師程序員
要成爲一個合格的Java程序猿,獨立完成需求是一個必須經歷的階段,在這個過程當中可能會遇到不少問題,要學會合理的利用資源(官方文檔、社區論壇等)去解決問題。在這個階段應該是踩坑最多、收穫最多、成長最快的階段。sql
在榆木入職的前3個月裏,作的都是一些改bug、完善需求的活,他不須要太多思考,根據客戶說的作就成了。三個月以後他的公司順利拿下了該客戶的二期項目,因爲人手不夠,再加上他在一期維護的時候對業務比較熟悉,老大便讓榆木獨自承擔該項目前置子系統的所有需求。剛開始的時候榆木是很激動的,隨之而來的倒是不知所措。json
榆木都是如何踩坑又填坑的呢?分享一下他的幾點經驗,但願對開發者有所幫助。後端
如何同時開啓兩個SVN服務服務器
由於公司資源不夠,老大就要求在原有的服務器上再弄一個SVN服務,因而他開始各類搗騰,但是無論怎麼樣就是沒有辦法同時起來兩個服務。怎麼辦,只能找哥哥(google)幫忙咯,由於SVN服務的啓動(/etc/init.d/svnserve start )是包含一些默認參數,如 --listen-port指定服務端口 默認是3691,若是要同時起兩個SVN服務只須要在啓動時指定兩個不一樣的listen-port就OK了。ide
以下,問題解決:svn
/etc/init.d/svnserve start -d -r /svn/repo 第一次第一個庫啓動 默認3691 /etc/init.d/svnserve --listen-port 9999 -d -r /svn/svndata 第二次指定端口啓動
問題搞定,緊接着就是緊張的代碼開發,事情有點想不到的順利,後端接口順利完工經過測試,榆木開始和前端對接聯調,好激動,搞很差能夠提早完成任務了。噼裏啪啦的搞完就開始測試了。
學習
fastJson序列化問題
所謂沒有遇到過bug的程序猿就不是正常的程序猿,一點都不意外,問題來了。同一個對象賦值給HashMap中不一樣的key 傳到前端後,第二個value居然不能被正常解析....... 他本身寫的代碼必須不能慫,有問題那就解決問題,因而榆木開始找問題所在,開始模擬數據,發現返回結果以下:
{"o1":{"age":16,"name":"o1"},"2":{"$ref":"$.o1"}}
很容易就能看出來,第二value在這個返回結果中用相似指針的方法("$ref":"$.o1")表示它和「o1」的值同樣,看起來像是同一個對象的循環引用哦,那是否是能夠把這個循環引用禁止呢?答案是能夠的。(有必要說明一下,這裏使用的是fastJson)經過SerializerFeature指定禁用循環依賴就能夠了。修改前代碼以下:
public static void test1() { TestObject object = new TestObject("o1", 16); Map<String, TestObject> map = new HashMap<String, TestObject>(); map.put("o1", object); map.put("o2", object); System.out.println(new String(JSON.toJSONBytes(map))); }
輸出結果:{"o1":{"age":16,"name":"o1"},"o2":{"$ref":"$.o1"}}
在一個集合對象中存在多條相同數據時,將ist集合對象轉化爲json對象輸出到前臺時,JSON默認對第二條數據處理時用"$ref":"$.object".<這裏object指第一條數據>,這樣的json轉化結果輸出到前臺確定是不可使用的,好在JSON有提供禁止關閉引用循環檢測的方法,只須要在轉化的時候加上SerializerFeature.DisableCircularReferenceDetect 就能夠解決了。修改後代碼以下:
public static void test1() { TestObject object = new TestObject("s1", 16); Map<String, TestObject> map = new HashMap<String, TestObject>(); map.put("o1", object); map.put("o2", object); SerializerFeature feature = SerializerFeature.DisableCircularReferenceDetect; System.out.println(new String(JSON.toJSONBytes(map, feature))); }
輸出結果以下:{"o1":{"age":16,"name":"o1"},"o2":{"age":16,"name":"o1"}}
到這裏問題就解決了。不久以後測試經過了,交付客戶測試版本,開始和中心聯調測試了。
OOM異常處理
榆木覺得到這裏就萬事大吉了,然而是不可能的。聯調測試兩天以後,客戶反饋說:「咱們的XXX報文數據已經往中心發過了呀,但是中心說他們沒有收到,大家查下是什麼問題唄!」客戶就是上帝呀,榆木和他的同事開始查詢日誌,發現有一些OOM的異常。異常產生的場景是在取數據-組報文-MQ轉發這個環節,而後就開始一個一個點的排查了。
榆木首先想到的可能緣由有:
一、數據取出來生成報文這個過程都是在內存中作的,會不會是這裏數據太多致使?
二、會不會是報文生成過程產生了過多Object沒有來得及回收?
三、會不會是數據發送慢於報文生成的速到致使等待隊列爆滿?
而後開始針對性的作修改測試,他將一次性取數據生成報文的過程改爲批量去作,而後測試運行一段時間沒有問題(排除 1);在生成保溫過程當中,將每個轉化後的對象置爲空Object=null,以便及時回收,測試運行一段時間沒有問題(排除2);在第三點上面,他最早想的是增長線程數量( 服務器開啓超線程、應用中增長線程數量)去提高處理速率,運行一段時間以後仍是會出現OOM。怎麼辦呢?再次回到了等待隊列上面來,能不能在必定程度上對等待隊列作個限制呢?因而榆木在每次從MQ取消息以前增長了對等待隊列的深度的判斷,若是深度大於最大線程數量的2倍,就放棄本次MQ隊列消息的處理。而後繼續測試,問題沒有再出現。
查詢慢怎麼辦?
最終項目上線了,終於能夠鬆一口氣了。但是有一天,榆木的老大說客戶反映部分查詢很慢,讓他去處理一下。榆木內心想着,這個應該是個小問題,給數據表加索引就能搞定。到了客戶現場以後發現,原來的表是有索引的,可查詢仍是慢,不得已只能去找緣由了。不得不說,explain SQL是個不錯的命令,發現索引沒有生效,通過仔細的比對,發現該關聯查詢的關聯字段在兩個表中都有索引, 兩個表的字符集都是UTF8,可是排序規則一個是utf-bin(二進制存儲數據,大小寫區分),一個是utf8_general_ci(大小寫不敏感),因此把數據排序規則改爲一致索引生效,查詢速度也就上來了。
PS: mysql中的UTF-8編碼的排序規則說明
utf8_bin將字符串中的每個字符用二進制數據存儲,區分大小寫。
utf8_genera_ci不區分大小寫,ci爲case insensitive的縮寫,即大小寫不敏感。
utf8_general_cs區分大小寫,cs爲case sensitive的縮寫,即大小寫敏感。
【寫在最後】
榆木整理了下這些年踩的坑,給本身也給正在和他掙扎在挨踢坑裏的小夥伴們一些啓發與鼓勵。持續學習是保持競爭力的前提;夯實的基礎是進階的墊腳石。擡頭走不獨行(exchange)、埋頭幹(code),就算被稱做屌絲,也仍是要有夢想,萬一逆襲了呢。
若是你也願意分享你的故事,請加51CTO開發者QQ交流羣 114915964聯繫羣主小官,期待你精彩的故事!