--------------------------------------------------------------------------------------------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/