今天看到一篇博文。如下:
經(jīng)常開(kāi)發(fā)java web應(yīng)用程序的朋友一定對(duì)有對(duì)程序打包,維護(hù)的經(jīng)驗(yàn),我們提高軟件的維護(hù)性一般可以從分離易變和不變的內(nèi)容,重構(gòu)軟件的結(jié)構(gòu)來(lái)實(shí)現(xiàn),重構(gòu)包括對(duì)代碼級(jí)別的,也包括對(duì)應(yīng)用程序目錄文件的重構(gòu),以下我就簡(jiǎn)單談?wù)勎业囊淮谓?jīng)歷。
我們一個(gè)系統(tǒng)是java web應(yīng)用程序,不過(guò)該系統(tǒng)所有的class文件、jsp、js、CSS、image文件和系統(tǒng)動(dòng)態(tài)上傳的doc、xls、image、wmv等文件放在同一個(gè)工程目錄中。具體目錄結(jié)構(gòu)如下:
應(yīng)用程序配置的上下文根目錄是/myappmyapp/WEB-INF/myapp/admin/myapp/images/myapp/style/myapp/files/myapp/videos/......
1、存在的問(wèn)題 實(shí)際上,files、videos文件夾,會(huì)在系統(tǒng)運(yùn)行中隨著用戶上傳發(fā)布新內(nèi)容經(jīng)常更新變化,而其他的部分都是在系統(tǒng)需求變更發(fā)布版本時(shí)才變化,這2種變化不同,前者是系統(tǒng)正常使用的結(jié)果,后者是系統(tǒng)變更的結(jié)果,根據(jù)變化與不變化分開(kāi)的原則,我們希望把這2類(lèi)文件夾分開(kāi)存放。 由于我們發(fā)布web應(yīng)用程序包(myapp.war)需要把應(yīng)用程序上下文根目錄myapp中的所有文件夾都包括進(jìn)來(lái),所以必須把經(jīng)常變化的files和videos文件夾移出去。
2、解決方法探索 我們知道,servlet中有兩種轉(zhuǎn)向的方法,一個(gè)是request.getRequestDispatcher(requestPath).forward(request,
response),它不改變url地址,直接轉(zhuǎn)發(fā)請(qǐng)求到一個(gè)jsp頁(yè)面,一個(gè)是resonse.sendRedirect(urlPath),它改版url地址,跳轉(zhuǎn)到另外一個(gè)請(qǐng)求。 不過(guò)其兩者都沒(méi)法訪問(wèn)servlet所在web應(yīng)用程序上下文(myapp)之外的目錄,而我們myapp內(nèi)的全部文件夾都需要打包成myapp.war,這樣就有個(gè)矛盾。 另外的方法,還有通過(guò)java.io.File使用流的方式讀一個(gè)文件,然后再使用servlet的response的out對(duì)象寫(xiě)出來(lái)傳送到頁(yè)面,這樣的方法可以訪問(wèn)到該web容器(如tomcat)所在操作系統(tǒng)的用戶根目錄(如windows的D:/或linux的/usr*等等),這樣我們就可以把files等文件夾移動(dòng)到myapp之外。比如tomcat的應(yīng)用程序上下文根目錄是/usr/tomcat/webapps/,我們讓files不在myapp中,而是放到/usr/upload/files中,這樣我們打包myapp.war的時(shí)候,就只裝載版本變化的最新版程序,用戶上傳文件都不變?nèi)匀环旁趂iles中,訪問(wèn)時(shí)使用File()來(lái)進(jìn)行。如: File f = new File("/usr/upload/files/2010/3/15/010001.doc")
使用FileInputStream來(lái)讀這個(gè)文件,然后使用response中的outstream來(lái)寫(xiě)這個(gè)文件流到頁(yè)面。 這其中存在的問(wèn)題是讀入的文件需要占用JVM的heap空間,如果文件有500MB大,那么一般的默認(rèn)JVM heap空間都是不夠的,需要在啟動(dòng)JVM的時(shí)候設(shè)置參數(shù),使之足夠容納這個(gè)文件,當(dāng)然文件的讀寫(xiě)都要使用JVM來(lái)調(diào)度,顯然效率會(huì)比較低,比直接讀取要慢很多。
3、方法的改進(jìn) 有沒(méi)有辦法,既能讓files文件夾在myapp之外,又不用File類(lèi)來(lái)讀文件數(shù)據(jù)流呢? 在linux中可以使用目錄的快捷方式來(lái)解決,實(shí)際上就是文件的軟連接功能。 大家都知道,在linux中文件和文件夾是同樣的數(shù)據(jù)實(shí)體,可以用同樣的方法來(lái)做連接,在另外一個(gè)地方操作某個(gè)文件或目錄的連接,就達(dá)到了直接操作這個(gè)文件或目錄的作用。 首先,建立軟連接的方式是: ln -s sourcePath destinationPath把files文件夾放在myapp之外,并且在webapps中的某一個(gè)應(yīng)用程序中,比如ROOT中建立一個(gè)files文件夾的軟連接,如: ln -s /usr/upload /usr/tomcat/webapps/ROOT/upload并且在ROOT的tomcat容器context中設(shè)定一個(gè)屬性 allowLinking="true" ,即可訪問(wèn) 這樣,我們可以在myapp應(yīng)用程序中,通過(guò)直接url調(diào)用,或者response的重定向方式,訪問(wèn)到files中的文件,如: http://localhost:8080/upload/files/2010/3/15/010001.doc 這樣一來(lái),訪問(wèn)upload快捷方式就等于是訪問(wèn)了upload文件夾,既沒(méi)有訪問(wèn)外在資源沒(méi)有權(quán)限限制,又沒(méi)有使用File類(lèi)讀寫(xiě)數(shù)據(jù)流,不會(huì)有heap空間的限制,我們實(shí)現(xiàn)了易變數(shù)據(jù)文件的分離,并且沒(méi)有內(nèi)存的消耗。讀寫(xiě)500MB的文件,不需要把heap空間設(shè)置到500以上,只需要默認(rèn)的64就可以了。 總結(jié)一下,利用操作系統(tǒng)的特性——軟連接,實(shí)現(xiàn)了數(shù)據(jù)文件從應(yīng)用程序包中的分離,操作系統(tǒng)實(shí)現(xiàn)目錄文件訪問(wèn)的跳轉(zhuǎn),利用了文件I節(jié)點(diǎn)的修改,這屬于操作系統(tǒng)底層實(shí)現(xiàn),比起利用JVM的應(yīng)用層類(lèi)File來(lái)實(shí)現(xiàn)讀寫(xiě)要快10倍以上,節(jié)約JVM Runtime空間。
4、總結(jié) 我為了讓web應(yīng)用程序易于維護(hù),把版本發(fā)布的程序代碼和用戶上傳的數(shù)據(jù)文件分離,經(jīng)過(guò)了對(duì)servlet重定向方法的思考和試驗(yàn),對(duì)File類(lèi)文件流方法的試驗(yàn),對(duì)linux文件軟連接的方法試驗(yàn),最好決定使用最后一種,同時(shí)解決了文件維護(hù)性和運(yùn)行效率的問(wèn)題。
思考部分:
如果是分布式怎么辦?如果數(shù)據(jù)和程序不在同一個(gè)服務(wù)器上怎么辦?
其實(shí)web容器都有一個(gè)自身的解決方法:虛擬目錄。如果數(shù)據(jù)不在程序?qū)?yīng)的那臺(tái)服務(wù)器上,只要在web容器中數(shù)據(jù)指向這個(gè)虛擬目錄就行。把虛擬目錄設(shè)置成共享。
新聞熱點(diǎn)
疑難解答
圖片精選