持續更新線上問題及解決方案

     最近線上遇到幾個小問題,排查代碼發現基本都是些細節問題,作些總結提示你們不要掉到坑中。前端

1、fastjson的序列化SerializerFeature使用注意                                       java

    咱們都知道Integer、Double、Boolean等包裝類型的字段默認值是null。若是不對這些字段設置值,那麼在反序列化時獲得的相應的值也應該是null。mysql

一、本次服務接口升級後致使調用方業務邏輯判斷失敗,好比Integer type 在升級以前是返回的null,而升級後返回了數字0。究其緣由發現同事在序列化時設置了SerializerFeature.WriteNullNumberAsZero,而致使Number類型(Boolean,Integer,Float,Double等)都轉成了0。sql

二、null屬性不顯示數據庫

咱們先來看一段代碼json

Map <String , Object > map = new HashMap<String , Object>();
map.put("a",1);
map.put("b","");
map.put("c",null);

String str = JSONObject.toJSONString(map);
System.out.println(str);//輸出結果:{"a":1,"b":""}

屬性c,怎麼就憑空消失了哪?這裏不講述緣由,只說明解決方案。vim

解決方案:使用fastjson的SerializerFeature序列化屬性,即JSONObject.toJSONString(Object object, SerializerFeature... features)windows

所以上面的代碼就能夠改寫成後端

String str = JSONObject.toJSONString(map,SerializerFeature.WriteMapNullValue);

 

咱們經常使用的SerializerFeature:數組

  • SerializerFeature.WriteMapNullValue, //輸出空置的字段
  • SerializerFeature.WriteNonStringKeyAsString,//若是key不爲String 則轉換爲String 好比Map的key爲Integer
  • SerializerFeature.WriteNullListAsEmpty,//list爲null時輸出[] 
  • SerializerFeature.WriteNullNumberAsZero,//number爲null時輸出0
  • SerializerFeature.WriteNullStringAsEmpty,//String爲null時輸出""
  • SerializerFeature.WriteNullBooleanAsFalse,//boolean爲null時輸出false
  • SerializerFeature.QuoteFieldNames,//輸出key時是否使用雙引號,默認爲true
  • SerializerFeature.DisableCheckSpecialChar//一個對象的字符串屬性中若是有特殊字符如雙引號,將會在轉成json時帶有反斜槓轉移符。若是不須要轉義,可使用這個屬性。默認爲false 

    總結:

    一、服務接口的測試用例覆蓋率仍是過低

    二、對於通用處理工具,在作修改時對工具類的來龍去脈要清楚

2、java.util.List.subList的陷阱                                                             

     咱們通常都會使用java.util.List中有一個subList方法,返回一個以fromIndex爲起始索引(包含),以toIndex爲終止索引(不包含)的一部分的視圖(List)。    

List<E> subList(int fromIndex, int toIndex);

    之因此說是視圖,是由於實際上,返回的list是靠原來的list支持的。原來的list和返回的list作的「非結構性修改」(non-structural changes),都會影響到彼此對方。

所謂的「非結構性修改」,是指不涉及到list的大小改變的修改。相反,結構性修改,指改變了list大小的修改。

若是發生結構性修改的是返回的子list,那麼原來的list的大小也會發生變化;

若是發生結構性修改的是原來的list(不包括因爲返回的子list致使的改變),那麼會是拋出一個ConcurrentModificationException。

  • 如何刪除一個list中的某個值   
list.remove(index)
  • 如何刪除一個list的某個區段
list.subList(int fromIndex, int toIndex).clear();
  • 如何修改子list視圖而不影響原來的list或修改原list而不影響子list視圖
List<Integer> subList2 = new ArrayList<Integer>(list2.subList(2, list2.size()));

 那麼ArrayList的remove的底層是怎麼作的?

      AbstractList中有一個屬性modCount,這個屬性是跟蹤list中數據被修改的次數,任何對list的add/remove操做,都將致使modCount++。

在AbstractList中還有一個內部類Itr implements Iterator,Itr是一個list遍歷的工具類。固然list.iterator()方法也是返回Itr對象,在Itr中有一個校驗位屬性expectedModCount;對於一個itr對象,其初始時expectedModCount=modCount。

      Iterator是list一個視圖,其最終仍是操做list的存儲結構。在使用iterator遍歷時,remove()操做,會致使modCount++(AbstractList.remove()),可是還有expectedModCount=modCount,即在iterator中remove數據,會帶來expectedModCount與modCount值的同步

在Iterator遍歷時,next(),remove()方法會校驗expectedModCount與modCount值是否一致,若是不一致,就意味着這list數據在iterator外部被修改,此時iterator遍歷將會形成ConcurrentModificationException.

      AbstractLlist不只支持普通的iterator,還支持ListIterator(ArrayList,LinkedList均支持),ListIterator增長了遍歷時雙向遊標能力(previous,next),增長了add方法。add方法和remove方法同樣也作了expectedModCount和modCount一致性校驗.

咱們來看下面四個對list數據刪除的代碼的區別

1)
for(int i=0;i<list.size();i++){
     list.remove(i);
}

2)
for(int i=list.size()-1;i>=0;i--){
    list.remove(i);
}

3)
int size = list.size();
for(int i=size-1;i>-1;i--){
     list.remove(i);
}

4)
for(Object i : list){
   //若是list中存在多個Object互相equals時,此方法仍然有效.注意list.remove(Object)內部使用了遍歷操做,並使用equals來比較對象並刪除.
   list.remove(i);
}

5) 
Iterator it = list.iterator()
while(it.hasNext()){
        it.next();
        it.remove();
}

1),2),3)是最普通的遍歷方式,可是在遍歷並有刪除操做時,彷佛它們執行的結果還有些差距,根據座標刪除,

那麼1)實事上只會有一半被刪掉,1)中每刪除一次,計算一次list.size(),可是當前i++,且前端刪除會形成數組結構copy。

2)後端刪除,不會形成copy,每次都是刪除最後一個位置,直至結束

3)由於size沒有從新計算,在刪除一半數據後,拋出IndexOutOfBoundsException

4)/5)正常

  

3、illegal character: \65279                                                               

   問題產生的操做過程,同事使用SVN提交java文件,發現有衝突使用UltraEdit進行了修改,從新編譯時卻報了異常

java:[1,0] illegal character: \65279 

   解決方法

   將文件從新保存成UTF-8 無BOM便可。

               

具體緣由參看高人的解釋

http://blog.csdn.net/shixing_11/article/details/6976900

 

4、Java線程池任務執行完畢後線程回收的問題 

咱們知道ThreadPoolExecutor解決了兩個重要的問題:

一、因爲減小了每一個任務調用的開銷,它們一般能夠在執行大量異步任務時提供加強的性能

二、還能夠提供綁定和管理資源(包括執行任務集時使用的線程)的方法。 

當使用java中的ThreadPoolExecutor,給咱們的工做帶來方便的同時,若是不當使用一樣也帶來巨大潛在危險。

最近在review代碼時,發現線程池中的全部任務執行完畢後,線程並無被銷燬。咱們知道初始化ThreadPoolExecutor會有構造參數

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

咱們看下TreadPoolExecutor是怎樣工做的

注意

      一、核心線程即core線程,只有當前線程數小於等於corePoolSize時,這時的線程才叫核心線程。

      二、在新任務被提交時,若是運行的core線程少於corePoolSize,才建立新core線程。並非一開始就建立corePoolSize個core線程。

      三、若是運行的線程多於corePoolSize 而少於 maximumPoolSize,則僅當隊列滿時才建立新線程。

      
按需構造
      核心線程最初只是在新任務到達時才被ThreadPoolExecutor建立和啓動的,可是也能夠手動調用方法 prestartCoreThread() 或 prestartAllCoreThreads()來的提早啓動核心線程。
    若是構造帶有非空隊列的池,這時則可能但願預先啓動線程。


保持活動時間

      若是線程池中當前線程數大於corePoolSize ,則這些多出的線程在空閒時間超過 keepAliveTime 時將會終止。
      若是後來線程池中線程變得更爲活動,則能夠建立新的線程。也可使用方法 setKeepAliveTime(long, java.util.concurrent.TimeUnit) 動態地更改此參數,若是把值設爲Long.MAX_VALUE TimeUnit.NANOSECONDS 的話,空閒線程不會被回收直到ThreadPoolExecutor爲Terminate。
      默認狀況下,此種活動策略只在有多於corePoolSize Threads的線程時纔會應用。可是隻要 keepAliveTime 值非 0,也能夠經過allowCoreThreadTimeOut(boolean) 方法也可將此超時策略應用於核心線程。
注意1:setKeepAliveTime(long, java.util.concurrent.TimeUnit)用於設置空閒線程最長的活動時間,即若是空閒時間超過設定值,就中止該線程,對該線程進行回收。
    該策略默認只對非內核線程有用(即當前線程數大於corePoolSize),能夠調用allowCoreThreadTimeOut(boolean)方法將此超時策略擴大到核心線程
注意2:若是把值設爲Long.MAX_VALUE TimeUnit.NANOSECONDS的話,空閒線程不會被回收直到ThreadPoolExecutor爲Terminate。

 

線程終止

      若是ThreadPoolExecutor在程序中沒有任何引用且沒有任何活動線程,線程池也不會自動 shutdown。
      若是但願確保回收線程(即便用戶忘記調用 shutdown()),則必須安排未使用的線程最終終止,設置適當保持活動時間,設置 allowCoreThreadTimeOut(boolean)。

    

關鍵函數

  • public void shutdown()

    按過去執行已提交任務的順序發起一個有序的關閉,可是不接受新任務。若是已經關閉,則調用沒有其餘做用。
    拋出:
        SecurityException - 若是安全管理器存在而且關閉此 ExecutorService 可能操做某些不容許調用者修改的線程(由於它沒有 RuntimePermission("modifyThread")),或者安全管理器的 checkAccess 方法拒絕訪問。

  • public List<Runnable> shutdownNow()

    嘗試中止全部的活動執行任務、暫停等待任務的處理,並返回等待執行的任務列表。在今後方法返回的任務隊列中排空(移除)這些任務。
    並不保證可以中止正在處理的活動執行任務,可是會盡力嘗試。 此實現經過 Thread.interrupt() 取消任務,因此沒法響應中斷的任何任務可能永遠沒法終止。
    返回:
        從未開始執行的任務的列表。 
    拋出:
        SecurityException - 若是安全管理器存在而且關閉此 ExecutorService 
        可能操做某些不容許調用者修改的線程(由於它沒有 RuntimePermission("modifyThread")),
        或者安全管理器的 checkAccess 方法拒絕訪問。

  • public int prestartAllCoreThreads()

    啓動全部核心線程,使其處於等待工做的空閒狀態。僅當執行新任務時,此操做才重寫默認的啓動核心線程策略。
    返回:
        已啓動的線程數

  • public boolean allowsCoreThreadTimeOut()

    若是此池容許核心線程超時和終止,若是在 keepAlive 時間內沒有任務到達,新任務到達時正在替換(若是須要),則返回 true。當返回 true 時,適用於非核心線程的相同的保持活動策略也一樣適用於核心線程。當返回 false(默認值)時,因爲沒有傳入任務,核心線程不會終止。
    返回:
        若是容許核心線程超時,則返回 true;不然返回 false

  • public void allowCoreThreadTimeOut(boolean value)

    若是在保持活動時間內沒有任務到達,新任務到達時正在替換(若是須要),則設置控制核心線程是超時仍是終止的策略。當爲 false(默認值)時,因爲沒有傳入任務,核心線程將永遠不會停止。當爲 true 時,適用於非核心線程的相同的保持活動策略也一樣適用於核心線程。爲了不連續線程替換,保持活動時間在設置爲 true 時必須大於 0。一般應該在主動使用該池前調用此方法。
    參數:
        value - 若是應該超時,則爲 true;不然爲 false 
    拋出:
        IllegalArgumentException - 若是 value 爲 true 而且當前保持活動時間不大於 0。

咱們瞭解了ThreadPoolExecutor後,確保線程的回收就能夠經過如下方式

// 在allowCoreThreadTimeOut設置爲true時,ThreadPoolExecutor的keepAliveTime參數必須大於0。
executor.allowCoreThreadTimeOut(true);
// 在任務執行完後,調用shutdown方法,將線程池中的空閒線程回收
executor.shutdown();

5、Eclipse下maven項目自動打war包丟失jar包問題解決方法 

 因爲本地沒有maven倉庫和強大的「牆」,使用maven命令clean package卻打包失敗。沒有辦法使用了Eclipse最原始的Export命令,打好包後發現依賴的jar都沒有打進去,真是一波三折呀。現說下完整的打包過程和解決方法:

一、選擇相應的profile,沒有配置略過

二、.project文件

檢查<buildSpec>節點中是否包含以下節點

<buildCommand>                    
     <name>
         org.eclipse.m2e.core.maven2Builder
     </name>
     <arguments></arguments>
</buildCommand>

檢查<natures>節點中是否包含以下節點

<nature>org.eclipse.m2e.core.maven2Nature</nature> 

三、.classpath文件

檢查是否包含以下節點

<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
        <attributes>
            <attribute name="maven.pomderived" value="true"/>
            <attribute name="org.eclipse.jst.component.dependency" value="/WEB-INF/lib"/>
        </attributes>
</classpathentry>

正由於沒有這個屬性<attribute name="org.eclipse.jst.component.dependency" value="/WEB-INF/lib"/>,才致使打的包中沒有jar。

上述三步驟檢查完畢後,從新clean工程,再打包就能夠正常了。

6、Linux系統下忽略代表大小寫

坑爹的DBA將線上的一個數據庫重作後,致使了某些服務拋出了大量的異常

select name,type,updateTime from VIDEO_INFO where 1=1  ExecuteData Table 'MS.VIDEO_INFO' doesn't exist

信息提示找不到代表。

咱們都知道Linux是區分代表大小寫,windows不區分代表大小寫的。DBA重作了新的數據庫後卻忽略設置代表大小寫。

解決方法很簡單:

編輯mysql配置文件:vi /etc/my.cnf

添加:lower_case_table_names=1 一句到文件中。

重啓MySQL,服務一切正常。

小結:重要的事情說一遍,檢查一遍。

7、swap file "*.swp" already exists!

Linux下咱們使用vi或vim對文件編輯, 當打開文件或保存文件可能會出現:

swap file "*.swp" already exists!

[O]pen Read-Only, (E)dit anyway, (R)ecover, (D)elete it, (Q)uit, (A)bort:

緣由:

使用vim編輯文件實際是先copy一份臨時文件並映射到內存給你編輯, 編輯的是臨時文件, 當執行:w後才保存臨時文件到原文件,執行:q後才刪除臨時文件。

每次啓動檢索式否有臨時文件, 有則詢問如何處理,就會出現如上情景。

解決方法:

一、顯示隱藏文件 ll -a或ls -a

二、刪除隱藏文件 rm -f *.swp

 

8、20880端口被佔用

問題描述:
在部署線上服務時,出現20880端口被佔用而沒法啓動。

問題分析:
20880端口被該服務器上的客戶端隨機選取源端口給佔用掉了。

解決方案:

一、使用net.ipv4.ip_local_port_range參數,規劃出一段端口段預留做爲服務的端口,這種方法是能夠解決當前問題,可是會有個問題,端口使用量減小了,當服務器須要消耗大量的端口號的話,好比反代服務器,就存在瓶頸了。
二、將服務監聽的端口以逗號分隔所有添加到ip_local_reserved_ports中,TCP/IP協議棧從ip_local_port_range中隨機選取源端口時,會排除ip_local_reserved_ports中定義的端口,所以就不會出現端口被佔用了服務沒法啓動。、

推薦使用第二種方法

$ cat /proc/sys/net/ipv4/ip_local_port_range
32000 61000
$ cat /proc/sys/net/ipv4/ip_local_reserved_ports
8080,9148 

 

 

 

 

因爲本人經驗有限,文章中不免會有錯誤,請瀏覽文章的您指正或有不一樣的觀點共同探討!

相關文章
相關標籤/搜索