Java開發問題總結

最近在做開發時,遇到了以下四個問題,總結一下。


1.Nginx+Struts中上傳文件的大小限制。

在上傳文件時,若文件太大會出現上傳失敗。我們的Web應用前端代理使用了Nginx,MVC使用了Struts,通過檢查,發現在Nginx和Struts中對於上傳文件的大小都做了限制。

在Nginx的http模塊配置中,client_max_body_size這個參數用於表示http請求body最大值,由於文件也是以二進制形式存儲於body中,因此client_max_body_size也限制了上傳文件的大小,當上傳文件較大時,可適當調大該值。

http {
    ...
    client_max_body_size 20m;
    ...
}

在Struts中,對於上傳文件大小也有限制,默認大小爲2M,可以在Struts配置文件中修改該默認配置。

<constant name="struts.multipart.maxSize" value="104857600"/>


2.Nginx代理對於請求響應超時時間的設置。

外部應用調用我們提供的http接口時,有時會返回504錯誤,但查看我們的應用日誌,請求是被正確執行了,再查看Nginx配置,發現在Nginx代理配置中,proxy_read_timeout這個參數用於表示Nginx轉發請求至實際應用後等待響應的時間,若超過該時間,則向客戶端返回超時錯誤,通過適當調大該值解決問題。


3.同一個應用部署在不同服務器上導致的亂碼問題。

爲了實現高可用,我們將同一個應用部署在兩臺服務器上,前端通過Nginx實現負載均衡。啓動後,發現應用有一個功能存在亂碼問題,該功能是接收客戶端傳來的GBK編碼字符串,並調用另一文件服務接口寫到另一個服務器的磁盤中。我們發現該亂碼問題只在客戶端請求轉發至這兩臺服務器上中的其中一臺時存在。

通過檢查發現,應用在接收客戶端傳來的GBK編碼字符串時,會調用字符串的getByte()方法,進行解碼,而getByte()方法在不指定編碼格式時,會使用服務器的默認編碼,而這兩臺服務器中,一臺服務器的默認編碼是GBK,能正確解碼,另一臺服務器的默認編碼是UTF-8,則不能正確解碼。

後來的解決方法是:由於應用是部署在resin容器中,因此修改resin的配置文件,在jvm參數配置中,指定服務器的默認編碼爲GBK。

<jvm-arg>-Dfile.encoding=GBK</jvm-arg>


4.Web應用負載過高

有一個Java Web應用部署在resin容器中,每隔一端時間,會導致服務器負載過高,但查看後臺並沒有報錯信息,因而以前每次只能通過重啓應用來解決該問題。

後來在網上找到一個解決該類問題的方法:

1)使用ps查看應用的進程id;

2)使用“top -H -p 進程id”查看進程中的線程列表,排在前面的線程佔CPU較高;

3)使用“jstack 進程id”dump Java進程使用情況;

4)從Java進程dump中找到在2)中排在前面佔CPU較高的線程,需要注意的是2)中的線程號是十進制,而3)中的線程號是十六進制,需要做一下轉換,我們找到的佔CPU較高的線程在dump中如下:

"http--8080-37$65442827" daemon prio=10 tid=0x00002aaabc0f3000 nid=0x2c53 runnable [0x000000004517b000]
java.lang.Thread.State: RUNNABLE
at java.util.HashMap.get(HashMap.java:303)
at com.sohu.cms.auth.impl.AuthenticationImpl.hasAction(AuthenticationImpl.java:170)
at com.sohu.cms.webapp.util.AuthenticationInterceptor.intercept(AuthenticationInterceptor.java:62)

發現問題發生在HashMap,這裏我們定義了一個static HashMap對象,在多個客戶端發起請求時,會併發訪問該HashMap對象時,由於HashMap並不是線程安全的,因而可能會引起問題。我們在網上找到一個問題現象和原因和我們類似的例子:http://shuaijie506.iteye.com/blog/1815213,其中提到問題根本原因是HashMap在resize時會造成死循環。

後來的解決方案是我們使用線程安全的ConcurrentHashMap來替代HashMap。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章