--------------------------------------------------------------------------------------------javascript
最近十幾天在作一個博客系統,由於域名服務器都閒置已久,因而乎決定合理利用起來,作個網站。系統總體架構採用分佈式的系統,也是當今不少企業都在用的,基於restful風格的一套系統。從父工程開始blog-parent.這是一個pom工程,主要用來放置pom.xml文件的,這個包含了整個項目全部依賴的jar包。而後是blog-common,這個存放項目中使用到的一些工具類,也是一個pom工程。而後是blog-manager工程,這個主要是後臺,包括用戶操做以及管理員操做,這個項目還有一個積分商城的功能,因此商城的後臺我也是放到這個manager工程裏面的。這個manager是一個pom工程,而後下面的mapper和pojo以及service和web都是一個maven module.而後除了blog-manager-web是一個war以外,剩下的三個都是jar工程。html
而後前臺是blog-portal,還有就是rest、search、sso、order。rest實際上是給積分商城用的。search使用的是一個solr集羣。由於服務器性能緣由,因此我搭建的是三臺tomcat的solr集羣,依託zookeeper來進行管理。java
sso就是單點登陸系統,主要給整個系統提供登陸服務的。order系統主要是給積分商城提供訂單服務的。mysql
下面來講詳細內容:nginx
1、系統後臺:web
後臺是一個easyui的界面,很是簡約,寫文章的富文本編輯器我採用的是kindeditor。這個編輯器性能仍是不錯的,其實使用百度的ueditor富文本編輯器效果也不錯,只是由於個人springmvc攔截配置的config一直不成功,因而我就換了其餘的編輯器。數據提交採用的post提交。redis
if(title==null || title==''){ alert("請輸入標題!"); }else if(typeId==null || typeId==''){ alert("請選擇博客類別!"); }else if(blogTypeId==null || blogTypeId==''){ alert("請選擇博客類別!"); }else if(content==null || content==''){ alert("請輸入內容!"); }else{ $.post("/mg/user/blog/save",{'username':username,'title':title,'typeid':typeId,'blogtypeid':blogTypeId,'content':content,'summary':summary,'contentNoTag':contentNoTag,'keyword':keyWord},function(result){ if(result.success){ alert("博客發佈成功!"); resetValue(); }else{ alert("博客發佈失敗!"); } },"json"); }
var content=itemAddEditor.html(); itemAddEditor.sync();
由於這裏是加入了lucene全文檢索功能,因此在添加或者修改文章的時候,都須要進行索引字段處理,由於富文本編輯器存到數據庫中的內容都是帶html標籤格式的,可是我檢索確定是不須要這些標籤的,因此使用下面的方法,來把這個html標籤去掉,放到contentNoTag字段,用於檢索。而content就是帶html標籤的須要存放在數據庫的內容。spring
var dd=content.replace(/<\/?.+?>/g,""); var contentNoTag=dd.replace(/(^\s*)|(\s*$)/g,"");//dds爲獲得後的內容
而後就是一個列表的查詢。由於這是一個多用戶的系統,因此每一個用戶查看的都是本身的博客信息,因此在查詢的時候須要加上用戶名。sql
對於添加博客的時候須要的一個lucene操做。數據庫
/**//** * 獲取IndexWriter實例 * @return * @throws Exception *//* */ private IndexWriter getWriter()throws Exception{ dir=FSDirectory.open(Paths.get("/home/tf/work/data/lucene1/")); //dir=FSDirectory.open(Paths.get("c:\\lucene")); SmartChineseAnalyzer analyzer=new SmartChineseAnalyzer(); IndexWriterConfig iwc=new IndexWriterConfig(analyzer); IndexWriter writer = null; try { writer = new IndexWriter(dir, iwc); } catch (Exception e) { writer.rollback(); e.printStackTrace(); } return writer; } /* *//** * 添加博客索引 * @param blog *//* */ public void addIndex(UBlog blog)throws Exception{ IndexWriter writer=getWriter(); Document doc=new Document(); doc.add(new StringField("id",String.valueOf(blog.getBlogid()),Field.Store.YES)); doc.add(new StringField("username",String.valueOf(blog.getUsername()),Field.Store.YES)); doc.add(new TextField("title",blog.getTitle(),Field.Store.YES)); doc.add(new StringField("releaseDate",DateUtil.formatDate(new Date(), "yyyy-MM-dd"),Field.Store.YES)); doc.add(new TextField("content",blog.getContentNoTag(),Field.Store.YES)); writer.addDocument(doc); writer.close(); }
而管理員就能夠查看全部用戶的文章,以及能夠進行凍結解凍操做。
後臺管理員這裏還有一個積分商城,主要是用戶發表博客以後又積分,積分能夠兌換K幣,而後K幣能夠兌換這個積分商城中的東西。這個商品添加以後,是不能直接在前臺進行查詢的,由於我對於這個商品時啓用了solr搜索服務的,在個人blog-search工程中作了一個定時任務,在天天凌晨兩點進行數據導入操做,系統導入完成以後,就能夠在前臺查看到添加的這些商品了。
/** * 導入商品數據庫到索引庫 */ @Scheduled(cron = "0 0 2 * * ?") //天天凌晨兩點執行 @RequestMapping("/import") public void importAllItems() { System.out.println("開始執行"); itemService.importAllItems(); System.out.println("執行結束"); }
關於整個後臺來講,界面很是簡約,須要的功能仍是基本上齊全的。由於是分佈式系統的,因此我上線的時候都是分開上線的,在上線後臺以後,這個規格參數和商品列表查詢一個不能及時刷新$("#itemList").datagrid("reload");我開始覺得是數據量太大致使刷新慢,後來發現並非。例如我操做了刪除,其實數據庫中的數據已經刪除了,可是這個datagrid卻沒有反應,查看狀態碼返回的是304.而後我想到了我後臺是啓動了CDN緩存加速的,因此我就又跑去看CDN的配置,而後就發現問題了,因而我把列表查詢的/mg/item目錄的刷新時間設置爲0,這樣就能夠及時刷新,再也不是一直在緩存。這樣這個問題就解決了。
經過這個事情我知道,在本地的localhost操做和上線真的是不一樣的。在上線過程還遇到了不少在本地操做沒有發生的事情,在本地都沒有什麼問題,一到雲服務器上部署,立刻問題就來了。哎!真是個磨人的小妖精。
前臺的話基本上也就這樣了,由於審美水平問題,只能作到這個樣子了,畢竟沒有美工,畢竟我是作系統架構和數據處理數據分析的,哎,看來仍是有不足之處啊。你們就將就着看吧,哈哈。 http://www.tianfang1314.cn/
前臺的這個博客文章我都加入到了redis緩存中,因此訪問速度理論上仍是提高了的。前臺頁面在CDN緩存設置的是一分鐘,因此後臺增刪改什麼的理論上是要過一分鐘以後,前臺纔會更新的。
關於前臺的lucene搜索就是:
/**//** * 查詢博客信息 * @param q 查詢關鍵字 * @return * @throws Exception *//* */ public List<UBlog> searchBlog(String q)throws Exception{ dir=FSDirectory.open(Paths.get("/home/tf/work/data/lucene1/")); IndexReader reader = DirectoryReader.open(dir); IndexSearcher is=new IndexSearcher(reader); BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder(); SmartChineseAnalyzer analyzer=new SmartChineseAnalyzer(); QueryParser parser=new QueryParser("title",analyzer); Query query=parser.parse(q); QueryParser parser2=new QueryParser("content",analyzer); Query query2=parser2.parse(q); booleanQuery.add(query,BooleanClause.Occur.SHOULD); booleanQuery.add(query2,BooleanClause.Occur.SHOULD); TopDocs hits=is.search(booleanQuery.build(), 100); QueryScorer scorer=new QueryScorer(query); Fragmenter fragmenter = new SimpleSpanFragmenter(scorer); SimpleHTMLFormatter simpleHTMLFormatter=new SimpleHTMLFormatter("<b><font color='red'>","</font></b>"); Highlighter highlighter=new Highlighter(simpleHTMLFormatter, scorer); highlighter.setTextFragmenter(fragmenter); List<UBlog> blogList=new LinkedList<UBlog>(); for(ScoreDoc scoreDoc:hits.scoreDocs){ Document doc=is.doc(scoreDoc.doc); UBlog blog=new UBlog(); blog.setBlogid(doc.get(("id"))); blog.setUsername(doc.get("username")); blog.setReleaseDateStr(doc.get(("releaseDate"))); String title=doc.get("title"); String content=StringEscapeUtils.escapeHtml(doc.get("content")); if(title!=null){ TokenStream tokenStream = analyzer.tokenStream("title", new StringReader(title)); String hTitle=highlighter.getBestFragment(tokenStream, title); if(StringUtil.isEmpty(hTitle)){ blog.setTitle(title); }else{ blog.setTitle(hTitle); } } if(content!=null){ TokenStream tokenStream = analyzer.tokenStream("content", new StringReader(content)); String hContent=highlighter.getBestFragment(tokenStream, content); if(StringUtil.isEmpty(hContent)){ if(content.length()<=200){ blog.setContent(content); }else{ blog.setContent(content.substring(0, 200)); } }else{ blog.setContent(hContent); } } blogList.add(blog); } return blogList; }
blog.setContent(new String(blog.getContent().getBytes("iso-8859-1"), "utf-8"));商品搜索這邊就是調用者blog-search的服務就能夠了。關於這個地方的細節我就再也不重複說了,今天只談架構。
由於這個系統先後十二個工程,其中mg作爲後臺,portal作的前臺,以及前面說到的各類。因此我須要一個nginx來處理,主要就是配置端口與域名的映射。在nginx中進行配置便可。固然,也是能夠直接使用tomcat熱部署直接傳到服務器中的tomcat中。固然若是用的是其餘中間件服務器的話配置也是相似的。就是域名端口號說明的。(爲了我服務器的安全,下面配置的端口號我修改了與我真實上線不一樣的端口號了),你也能夠按本身的實際狀況配置。總之仍是很是實用和簡單的。
upstream manager.tianfang1314.cn{ server 139.199.158.214:9100; } upstream rest.tianfang1314.cn{ server 139.199.158.214:9101; } upstream search.tianfang1314.cn{ server 139.199.158.214:9101; } upstream sso.tianfang1314.cn{ server 139.199.158.214:9103; }
server { listen 80; server_name manager.tianfang1314.cn; location / { proxy_pass http://manager.tianfang1314.cn; index index.html index.htm; } } server { listen 80; server_name rest.tianfang1314.cn; location / { proxy_pass http://rest.tianfang1314.cn; index index.html index.htm; } } server { listen 80; server_name search.tianfang1314.cn; location / { proxy_pass http://search.tianfang1314.cn; index index.html index.htm; } } server { listen 80; server_name sso.tianfang1314.cn; location / { proxy_pass http://sso.tianfang1314.cn; index index.html index.htm; } }
我遇到最煩人的問題就是這個圖片上傳的問題,我原本是使用的FTP進行圖片上傳的。而後這個磨人的小妖精在本地上傳的時候沒有任何問題,我一部署到服務器,完了,徹底post不上去了,折騰的夠嗆,一直是什麼network什麼什麼的錯誤,簡直氣炸!我後來慢慢的排查問題,從文件大小限制,nginx配置,cdn緩存配置,服務器權限,還從朋友那裏在接了一臺tomcat來測試,發現依然是這個問題。折騰了一天。而後我就放棄治療。
Uncaught SecurityError: Failed to read the 'contentDocument' property from 'HTMLIFrameElement': Blocked a frame with origin "http://manager.tianfang1314.cn" from accessing a frame with origin "null". The frame requesting access has a protocol of "http", the frame being accessed has a protocol of "data". Protocols must match.
固然,最後我換了一種方案,原本這個圖片是所有要存放到個人ftp圖片服務器中的。既然解決不了怎麼辦呢,這個圖片上傳的功能是必需要的啊,因而乎,我忽然發現了COS對象存儲服務。簡直髮現寶有木有。開始看文檔感受挺複雜的,後來本身折騰了一下,將這個cos的官方代碼於個人ftp工具類的代碼進行了整合,哎呦喂,竟然成功了,1個小時就搞定了。在這一篇博客中介紹如何使用騰訊雲的COS對象存儲服務。
網站的訪問網址就是: http://www.tianfang1314.cn/