前言:準備了大半年的東西,在上一個星期準備交付給客戶使用。怎麼交付給他們使用呢?就是把咱們程序員寫的的代碼發佈到客戶那邊的服務器環境中去。固然對於項目的功能咱們已經測試屢次了,本覺得很簡單的事情卻苦苦折磨了咱們一個星期多才算有個頭緒,並且還把以前一些功能屏蔽了。下面就把我這個星期的bug簡單記錄一下,這其中有我本身代碼的問題也有客戶環境那邊致使的問題。html
測試環境 java
第一階段 LINUX服務器 + tomcat war項目直接部署測試程序員
第二階段 k8s + docker web
生產環境正則表達式
騰訊雲平臺 docker + tsf平臺spring
其中我有個項目是用spring boot開發的,並且仍是個war項目,其餘的都是jar。恰恰就是這個項目在部署到客戶那邊常常出現異常重啓的現象。當時仍是測試階段,數據量還很小,天天就會重啓個8-10+次。這一現象可驚呆了鄙人(由於我第一感受就是死循環,內存泄漏致使的<嚴重並且低級>,由於這會嚴重影響個人聲譽^_^),故要急需解決。由於有一塊我用到了設計模式中的思想(單例+枚舉),來實現模板消息的發送。及恐慌是這一塊致使的。後通過排查雖然有些地方能夠優化。但不至於把項目搞死。無奈只好把問題拋出來,你們一塊兒想辦法解決,雖都提出了不少想法但都沒有最終解決問題,後讓客戶那邊幫咱們查看容器重啓的緣由,他告訴咱們cup佔用率太高。後來咱們把項目的啓動參數增長到2核+內存2014M(以前1核+512M),問題解決。docker
1.檢測是否因內存泄漏致使設計模式
工具 postman + jvisualvm.exe 經過postman連續發送5000次請求,而且經過查看 jvisualvm 界面查看內存使用狀況(好像是512M會自動觸發java的回收GC機制)tomcat
postman 設置連續發送屢次請求服務器
jvisualvm( 目錄 **\jdk1.8.0_121\bin ) 查看內存佔用狀況
備註:可經過此工具來查看內存泄漏,cup佔用等狀況。512M觸發java回收機制
備註:由於垃圾回收機制的影響,若是若是給項目分配了較少的資源,就會頻繁調用GC,反而影響性能。而若是分配的核數較少更會影響項目的穩健型
2.單例的思考
單例對象應該是做爲工具對象,即該對象的屬性不該該所有能修改(要否則就失去的單例的意義),然對於不常常修改的屬性不該該影響其餘其餘人(其餘線程)使用。故我總結一下幾點設計單例的規則(已個人消息模板爲例)
* > 可選屬性(參數) —— 消息的內容,好比你中的那個獎品。可選屬性應該是每次使用都會被從新賦值,即對於一個可選屬性都應該有一個默認值。
*> 不可選屬性(參數 ) —— 消息發送的URL,和消息發送的應用APPID(必須有否則就失去單例的意義)
兩類屬性應該分來建立
有人說設計單例時應該考慮內存泄漏,我認爲是有道理的,應爲單例對象是用static關鍵字修飾,故其不會被垃圾回收機制GC回收到。若是其單例對象的屬性引用了大量的資源如一個map。就會致使該map也不會被回收到
因爲涉及IO流問題,就少不了編碼方式,即常出現的中文亂碼問題。針對這一塊,其實有出現多個問題,但總結起來就兩類問題,一類就是中文亂碼,另外一類就是客戶端那邊的網絡限制。真對問題的出現緣由,一方面就是編寫代碼的時候我欠思考,固然這是很難避免的,對於IO流的這一塊的複雜程度你們也是有目共睹的^_^。另外一方便就是咱們設計的時候沒有想打客戶那邊的網絡限制,固然就算想到了估計也要這麼作^_^。下面對該問題的詳細描述。
客戶網絡限制
無論需求先給介紹一下發生場景,我作了一個編輯器(uedit百度的),客戶能夠利用該編輯器編輯文章,並生成HTML頁面。其中該編輯器中的大部分圖片可能都來自網絡,而我須要把網絡圖片下載到本地,下次顯示的時候就是咱們本身資源庫中的圖片。步驟以下
步驟一》一個正則表達式獲取該文章中的全部圖片地址
public static List<String> searchPicUrlInString(String html) { List<String> urlList = new ArrayList<String>(); // 圖片網址正則 String reg = "(http:|https:){1}[\\//]{2}[A-Za-z0-9.\\/\\-\\_\\%]+(bmp|jpeg|gif|png|jpg)"; Pattern pattern = Pattern.compile(reg); Matcher matcher = null; matcher = pattern.matcher(html); while (matcher.find()) { String imageUrl = matcher.group(0); // 若是圖片已在chinatopfine,不返回 if (imageUrl.contains("chinatopfine")) { continue; } // 若是圖片tobacco,不返回 if (imageUrl.contains("tobacco")) { continue; } // mmbiz.qpic.cn"服務器不返回 if (imageUrl.contains("mmbiz.qpic.cn")) { continue; } urlList.add(imageUrl); } return urlList; }
步驟二》經過網絡圖片URL下載圖片,並返回咱們圖片地址(咱們本身的)
/** * * @param picUrl * 網絡圖片地址 * @param savePath * 保存路徑 * @return 返回圖片名稱 */ public static String savePictureByUrl(String picUrl, String savePath) { // 生成圖片名 String imgType = picUrl.substring(picUrl.lastIndexOf(".")); String imgName = UUID.randomUUID().toString().replaceAll("-", "") .toString() + imgType; File f = new File(savePath); if (!f.exists()) { f.mkdirs(); } String imgPath = savePath + File.separator + imgName; File file = new File(imgPath); URL url = null; try { url = new URL(picUrl); DataInputStream dataInputStream = new DataInputStream( url.openStream()); FileOutputStream fileOutputStream = new FileOutputStream(file); ByteArrayOutputStream output = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int length; while ((length = dataInputStream.read(buffer)) > 0) { output.write(buffer, 0, length); } fileOutputStream.write(output.toByteArray()); dataInputStream.close(); fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); log.info("本地備份圖片失敗:" + picUrl); } // 返回圖片名 return imgName; }
但因爲客戶那邊限制的訪問外網的能力,最終這個功能被咱們捨棄了
中文亂碼
其實中文亂碼發生在兩處,第一處就是前面說的編輯器中的文章不免會有中文,當時就是中文亂碼。當時我用的是FileOutputStream,簡單查看了一下該類不能指定編碼格式,但其的包裝類PrintStream能夠指定,這兩個問題也就很快解決了
代碼以下 new PrintStream(new FileOutputStream(file),false, "UTF-8");
public static String saveHtmlByString2(String htmlName,String htmlDir, String content, File templateHtml) throws IOException { // 生成html名和地址 htmlName = htmlName + ".html"; String htmlPath = htmlDir + htmlName; Document doc; try { doc = Jsoup.parse(templateHtml, "UTF-8"); } catch (IOException e) { log.info("Html 模板解析錯誤:" + templateHtml.getAbsolutePath()); throw e; } // 獲取模板中要 替換的標籤 Element div = doc.getElementById("isme"); // content替換爲content div.html(content); // 建立文件 File filedir = new File(htmlDir); if (!filedir.exists()) { filedir.mkdirs(); } // 建立文件 File file = new File(htmlPath); file.createNewFile(); PrintStream ps = new PrintStream(new FileOutputStream(file),false, "UTF-8"); ps.println(doc.html()); ps.flush(); ps.close(); return htmlName; }
另外一處就是上傳帶有中文名的圖片(固然能夠直接修改圖片名稱把中文名替換,但由於某種業務緣由須要使用原圖片的名稱),我直接把war包發佈到咱們的測試環境即linix服務器是能夠上傳中文名圖片的爲何放到客戶的環境就很差用了呢?
方法一 首先想到的方法就是和上面的解決思路同樣,經過包裝流指定編碼方式(很差用)
方法二 指定服務器編碼格式(echo $LANG查看),固然也是很差用(其實關鍵點,就是這)
方法三 網上有人說文件的內容和System.getProperty(「file.encoding」)這個屬性有關而文件名和System.getProperty(「sun.jnu.encoding」)有關,固然測試結果仍是很差用。
方法四 修改docker容器內的編碼格式,有這個想法時,我首先是進入了咱們本身的容器經過(echo $LANG)命令發現沒有輸出,固然容器內的中文名圖片也是亂碼。感受一會兒問題就找到了根源。即個人項目的真正運行環境是容器。而後就從新修改建立鏡像的DockerFile以下問題解決^_^
WORKDIR /home ADD wxcms.war /home/tomcat/webapps/ ADD myhosts /home/ ADD startup.sh /home/tomcat/bin/startup.sh # 安裝必要的工具 RUN chmod -R 777 /home/tomcat/bin && \ mkdir -p /qyhupload/wxkf && \ mkdir -p /qyhupload/cyxx && \ mkdir -p /qyhupload/information && \ mkdir -p /qyhupload/company/ && \ mkdir -p /qyhupload/cigarette/ && \ mkdir -p /qyhupload/article && \ mkdir -p /qyhupload/tuwenmessage && \ ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo Asia/Shanghai > /etc/timezone RUN locale RUN localedef -i zh_CN -c -f UTF-8 zh_CN.UTF-8 RUN echo "export LC_ALL=zh_CN.UTF-8" >> /etc/profile && source /etc/profile ENV LANG zh_CN.UTF-8 ENV LC_CTYPE zh_CN.UTF-8
這塊內容纔是我最想總結的對本身的教訓, 有時候我爲了圖一時之便,把本來設計的規則棄置無論,也許當時本身還能夠控制但時間久了,或遇到本身沒法把控的問題出現。就會焦頭爛額,沒法下手。這一次有一個問題就狠狠的敲擊了我。
長哎一聲,以致我當時的哀傷。事情簡單介紹一下,以前爲了方便本身查看bug,常常會把測試環境的代碼鏈接到本身的本地項目。雖然在當時時時段段會有點影響,但我圖方便就臨時啓動一下本地項目,也就問題解決了(當時就兩套環境),本身感受還挺方便。因此就僥倖一直這樣。但此次上真正環境,出現了一個問題驚動了咱們的小團隊,須要快熟解決..................不說了,浪費了不少時間也無從下手,以前僥倖獲取的方便也一會兒全還了回去。