beetl-bbs社區(qū)論壇系統(tǒng)分析+Elastic Search

一辆憔、介紹

1.1、項(xiàng)目描述

bbs論壇系統(tǒng)是一個(gè)相對(duì)比較輕量級(jí)的java社區(qū)系統(tǒng)报嵌,上手簡(jiǎn)單虱咧、學(xué)習(xí)成本低、比較容易擴(kuò)展锚国,主要包括用戶注冊(cè)腕巡、登錄、退出血筑、發(fā)帖回帖绘沉、主頁(yè)、貼子置頂刪除等功能云挟。其中還具有全局用戶錯(cuò)誤處理梆砸、語(yǔ)法直觀,性能高超,功能能齊全的模板引擎和強(qiáng)大的搜索技術(shù)等特點(diǎn)园欣。

1.2帖世、項(xiàng)目特點(diǎn)

bbs論壇系統(tǒng)采用spring boot2,Spring Cache沸枯,Elastic Search日矫,Beetl,BeetlSQL框架基于MVC機(jī)制開(kāi)發(fā)的一套簡(jiǎn)易的社區(qū)論壇系統(tǒng)绑榴,可拿來(lái)即用哪轿。強(qiáng)大、靈活的搜索滿足絕大部分社區(qū)用戶的需求翔怎。支持MySQL窃诉、Oracle等主流數(shù)據(jù)庫(kù)杨耙。

Elastic Search : 是一個(gè)分布式可擴(kuò)展的實(shí)時(shí)搜索和分析引擎,一個(gè)建立在全文搜索引擎 Apache Lucene(TM) 基礎(chǔ)上的搜索引擎.當(dāng)然 Elasticsearch 并不僅僅是Lucene 那么簡(jiǎn)單,它不僅包括了全文搜索功能飘痛,還可以進(jìn)行以下工作珊膜;分布式實(shí)時(shí)文件存儲(chǔ),并將每一個(gè)字段都編入索引宣脉,使其可以被搜索车柠。實(shí)時(shí)分析的分布式搜索引擎∷懿可以擴(kuò)展到上百臺(tái)服務(wù)器竹祷,處理PB級(jí)別的結(jié)構(gòu)化或非結(jié)構(gòu)化數(shù)據(jù)。Elastic Search的兩個(gè)基本特征就是儲(chǔ)存和索引羊苟,Elasticsearch是面向文檔的塑陵,是面向文檔型數(shù)據(jù)庫(kù),即一條數(shù)據(jù)就是一個(gè)文檔蜡励,它可以存儲(chǔ)整個(gè)對(duì)象或文檔猿妈。然而它不僅僅是存儲(chǔ),還會(huì)索引每個(gè)文檔的內(nèi)容使之可以被搜索巍虫。在Elasticsearch中,你可以對(duì)文檔(而非成行成列的數(shù)據(jù))進(jìn)行索引鳍刷、搜索占遥、排序、過(guò)濾输瓜。這種理解數(shù)據(jù)的方式與以往完全不同瓦胎,這也是Elasticsearch能夠執(zhí)行復(fù)雜的全文搜索的原因之一,同時(shí)具有強(qiáng)大的索引能力使得大大提升了搜索性能尤揣。因此搜索便是Elastic Search 最核心功能搔啊。后面的發(fā)帖模塊的分析會(huì)給出Elastic Search的基本使用,也可翻閱官方api地址:https://es.xiaoleilu.com/

Beetl :java模板引擎北戏,Beetl相對(duì)于其他java模板引擎负芋,具有功能齊全,語(yǔ)法直觀,性能超高嗜愈,以及編寫(xiě)的模板容易維護(hù)等特點(diǎn)旧蛾。使得開(kāi)發(fā)和維護(hù)模板有很好的體驗(yàn)。Beetl的核心是GroupTemplate蠕嫁,是一個(gè)重量級(jí)對(duì)象锨天,實(shí)際使用的時(shí)候建議使用單模式創(chuàng)建,創(chuàng)建GroupTemplate需要倆個(gè)參數(shù)剃毒,一個(gè)是模板資源加載器病袄,一個(gè)是配置類搂赋,可參考:http://ibeetl.com/guide/#beetl

BeetlSQL:BeetSql是一個(gè)全功能DAO工具類似我們常用的ORM框架Mybatis、Hibernate 益缠、JPA脑奠,但BeerlSQL同時(shí)具有Hibernate 和 Mybatis優(yōu)點(diǎn)功能,適用于承認(rèn)以SQL為中心左刽,同時(shí)又需求工具能自動(dòng)能生成大量常用的SQL的應(yīng)用捺信。使用大量?jī)?nèi)置SQL,SQL 模板基于Beetl實(shí)現(xiàn)欠痴,更容易寫(xiě)和調(diào)試迄靠,以及擴(kuò)展可以針對(duì)單個(gè)表(或者視圖)代碼生成pojo類和sql模版,甚至是整個(gè)數(shù)據(jù)庫(kù)喇辽。能減少代碼編寫(xiě)工作量提高開(kāi)發(fā)效率掌挚。其它可參考:http://ibeetl.com/guide/#beetlsql

對(duì)于大部分開(kāi)發(fā)者這3個(gè)框架也許是我們用得最少的,因此這個(gè)社區(qū)論壇系統(tǒng)也是我們學(xué)習(xí)Elastic Search菩咨,Beetl吠式,BeetlSQL這幾個(gè)框架很好的一個(gè)demo

1.3 、項(xiàng)目介紹

此社區(qū)系統(tǒng)為單模塊應(yīng)用抽米,未實(shí)行前后端分離特占,前后端交互是采用MVC? ModelAndView 的那套機(jī)制與原理,使用ModelAndView類用來(lái)存儲(chǔ)處理完后的結(jié)果數(shù)據(jù)云茸,以及顯示該數(shù)據(jù)的視圖是目。業(yè)務(wù)處理器調(diào)用模型層處理完用戶請(qǐng)求后,把結(jié)果數(shù)據(jù)存儲(chǔ)在該類的model屬性中标捺,把要返回的視圖信息存儲(chǔ)在該類的view屬性中懊纳,然后讓該ModelAndView返回該Spring?MVC框架對(duì)該對(duì)象進(jìn)行解析,最后把結(jié)果數(shù)據(jù)顯示在指定的頁(yè)面上亡容。?

1.4嗤疯、本地部署

.??環(huán)境要求:jdk1.8+ 、MySql5.5+

. 創(chuàng)建表database?數(shù)據(jù)庫(kù)編碼為 UTF-8

. 執(zhí)行數(shù)據(jù)庫(kù)腳本闺兢,執(zhí)行 install-mysql.sql 文件茂缚,初始化數(shù)據(jù)

. git clone 項(xiàng)目 運(yùn)行 BbsMain.class類

.項(xiàng)目訪問(wèn)路徑:http://localhost:8084/bbs/bbs/index

.賬號(hào)密碼:admin/123456

.必須安裝elastic search 作為全文搜索:?

下載?https://www.elastic.co/downloads/past-releases/elasticsearch-5-4-3(更高級(jí)的版本目前暫時(shí)未驗(yàn)證通過(guò)),進(jìn)入bin目錄,調(diào)用elasticsearch 啟動(dòng)

.安裝elastic search 分詞:進(jìn)入bin目錄列敲,運(yùn)行 ./elasticsearch-plugin install analysis-smartcn

然后重新啟動(dòng)elasticsearch

注:本地部署測(cè)試只需要安裝elasticsearch阱佛,然后cmd進(jìn)入安裝elasticsearch的bin目錄調(diào)用elasticsearch啟動(dòng)即可進(jìn)行發(fā)帖搜索等相關(guān)功能實(shí)踐

二、項(xiàng)目后端源碼分析

2.1戴而、分析項(xiàng)目的思路

當(dāng)我們熟悉一個(gè)項(xiàng)目時(shí)凑术,我們首先應(yīng)該要清楚的知道這個(gè)項(xiàng)目運(yùn)用了哪些技術(shù),其次我們就需要知道這個(gè)項(xiàng)目是怎么樣進(jìn)行數(shù)據(jù)交互的(即:前后端是怎樣進(jìn)行數(shù)據(jù)交互的)所意,再深層次我們則可了解設(shè)計(jì)原理淮逊、思路以及其它相關(guān)技術(shù)的原理等等催首。(下面的登錄模塊詳細(xì)介紹了其交互流程)

?2.2 、登錄模塊

在首頁(yè)index.html頁(yè)面中引入了layout.html,如下:

在layout.html中有如下一段代碼泄鹏,其采用了ajax方式登錄

在后臺(tái)登錄接口

核心代碼有兩行:

1郎任、?BbsUser user =bbsUserService.getUserAccount(userName, password);通過(guò)用戶名和密碼檢測(cè)獲取用戶信息

2、WebUtils.loginUser(request, response, user, true); 將用戶信息set到cookie使戶登陸狀態(tài)維持备籽,cookie設(shè)計(jì)為:des(私匙).encode(userId~time~maxAge~password~ip),并且為了保證安全同時(shí)將cookie指定為httpOnly

2.3舶治、發(fā)帖模塊

2.3.1、首先我們安裝 Elasticsearch后安裝Marvel车猬,Marvel是Elasticsearch的管理和監(jiān)控工具霉猛,在開(kāi)發(fā)環(huán)境下免費(fèi)使用。它包含了一個(gè)叫做Sense的交互式控制臺(tái)珠闰,使用戶方便的通過(guò)瀏覽器直接與Elasticsearch進(jìn)行交互惜浅。安裝Marvel不是必須的,但是它可以通過(guò)在你本地Elasticsearch集群中運(yùn)行示例代碼伏嗜。Marvel是一個(gè)插件坛悉,可在Elasticsearch目錄中運(yùn)行以下命令來(lái)下載和安裝:

./bin/plugin -i elasticsearch/marvel/latest

echo'marvel.agent.enabled: false'>> ./config/elasticsearch.yml? ? ?這個(gè)命令則是關(guān)閉監(jiān)控

運(yùn)行?Elasticsearch啟動(dòng)速度會(huì)有點(diǎn)慢耐心等待會(huì),其索引地址默認(rèn)端口為9200

web管理界面head 安裝 進(jìn)入bin目錄下承绸,打開(kāi)cmd裸影,進(jìn)入dos界面 輸入:plugin install mobz/elasticsearch-head 進(jìn)行下載,注冊(cè)服務(wù)進(jìn)入bin目錄下军熏,打開(kāi)cmd空民,進(jìn)入dos界面 依次輸入: service.bat install service.bat start 成功之后,再輸入 services.msc 跳轉(zhuǎn)到Service服務(wù)界面羞迷,可以直接查看es的運(yùn)行狀態(tài)!?

訪問(wèn)http://localhost:9200/頁(yè)面顯示有東西則表示啟動(dòng)成功,接下來(lái)我們可以開(kāi)始各種實(shí)驗(yàn)了我們?cè)陧?xiàng)目的pom文件中加入elasticsearch依賴

2.3.2画饥、我們?cè)陧?xiàng)目的配置文件 application.properties 配置Elasticsearch的地址

#Elasticsearch

spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300

#Elasticsearch bbs索引地址

elasticsearch.bbs.content.url=http://127.0.0.1:9200/bbs/content/_search

2.3.3衔瓮、Elasticsearch 模板配置,這個(gè)模板根據(jù)自己的需求自行配置下面以發(fā)帖為例簡(jiǎn)述其使用,當(dāng)然這里只是最基本的一些使用抖甘,高級(jí)點(diǎn)的還有分布式集群热鞍、聚合以及索引的種類等想要了解更多的可參考官方文檔或相關(guān)博客。

發(fā)帖模塊算是整個(gè)論壇系統(tǒng)的核心模塊之一衔彻,這里用spring AOP技術(shù)(此處使用的是AOP的環(huán)繞通知@Around 在這里其底層使用的是cglib動(dòng)態(tài)代理)通過(guò)匹配持有指定注解@EsIndexs或@EsIndexType的方法實(shí)現(xiàn)了創(chuàng)建索對(duì)象引并且Elasticsearch數(shù)據(jù)保存薇宠。我們也以發(fā)帖模塊為例來(lái)說(shuō)明項(xiàng)目中如何來(lái)使用Elasticsearch?

@EsIndexType和EsIndexs中用@Document標(biāo)記,表示這個(gè)類為文檔文檔對(duì)象艰额,@Document注解是Elasticsearch的常用的注解之一澄港,其常用的屬性有①indexName:對(duì)應(yīng)索引庫(kù)名稱;②type:對(duì)應(yīng)在索引庫(kù)中的類型柄沮;④shards:分片數(shù)回梧;replicas:副本數(shù)废岂;indexStoreType:索引文件存儲(chǔ)類型;createIndex:是否創(chuàng)建索引狱意。另外Elasticsearch還有@Field湖苞,作用在成員變量,標(biāo)記為文檔的字段并制定映射屬性详囤。@Id:作用在成員變量财骨,標(biāo)記一個(gè)字段為id主鍵;一般id字段或是域不需要存儲(chǔ)也不需要分詞藏姐;type:字段的類型隆箩,取值是枚舉FieldType;index:是否索引包各,布爾值類型摘仅,默認(rèn)是true;store:是否存儲(chǔ)问畅,布爾值類型娃属,默認(rèn)值是false;analyzer:分詞器名稱护姆,一般情況下Elasticsearch都會(huì)把該字段存儲(chǔ)到Field域中矾端,只是當(dāng)store=?false時(shí),默認(rèn)設(shè)置卵皂;那么給字段只存儲(chǔ)在"_source"的Field域中秩铆,當(dāng)store = true時(shí),該字段的value會(huì)存儲(chǔ)在一個(gè)跟_source平級(jí)的獨(dú)立Field域中灯变;同時(shí)也會(huì)存儲(chǔ)在_source中殴玛,因此,我們?cè)谑褂胹tore時(shí)需注意以下兩點(diǎn):(1)source field在索引的mapping中disable了添祸。這種情況下如果不將某個(gè)field定義成store=true滚粟,那些將無(wú)法在返回的查詢結(jié)果中看到這個(gè)field;(2)__source的內(nèi)容非常大我們想要在返回的_source document中解釋出某個(gè)field的值的話,開(kāi)銷會(huì)很大(當(dāng)然你也可以定義source filtering將減少network overhead)刃泌,比例某個(gè)document中保存的是一本書(shū)凡壤,所以document中可能有這些field: title, date, content弦赖。假如我們只是想查詢書(shū)的title 跟date信息疗涉,而不需要解釋整個(gè)_source(非常大),這個(gè)時(shí)候我們可以考慮將title, date這些field設(shè)置成store=true厅瞎。(3)雖然ield store可以減少查詢的開(kāi)銷但其實(shí)這樣也會(huì)加大disk的訪問(wèn)頻率俗扇。假如你將_source中的10個(gè)field都定義store硝烂,那么在你查詢這些field的時(shí)候會(huì)將會(huì)有10次disk seek的操作。而返回_source只有一次disk seek的操作铜幽。所以這個(gè)也是我們?cè)诙x的時(shí)候需要blance的钢坦。

基本用法:

@Field(index=true)表示是否索引究孕;

@Field(analyzer="ik_max_word",searchAnalyzer="ik_max_word")表示是否分詞,如果是分詞就會(huì)按照分詞的單詞搜索爹凹,如果不是分詞就按照整體搜索

@Field(store=true)是否存儲(chǔ)厨诸,也就是頁(yè)面上顯示。

注:analyzer在Elasticsearch中扮演者分析器的角色禾酱,在全文搜索中微酬,詞是一個(gè)搜索單元,表示文本中的一個(gè)詞颤陶,標(biāo)記表示在文本字段中出現(xiàn)的詞颗管,由詞的文本、在原始文本中的開(kāi)始和結(jié)束偏移量滓走、以及數(shù)據(jù)類型等組成垦江。ElasticSearch 把文檔數(shù)據(jù)寫(xiě)到倒排索引(Inverted Index)的結(jié)構(gòu)中,倒排索引建立詞(Term)和文檔之間的映射搅方,索引中的數(shù)據(jù)是面向詞比吭,而不是面向文檔的。分析器(Analyzer)的作用就是分析(Analyse)姨涡,用于把傳入Lucene的文檔數(shù)據(jù)轉(zhuǎn)化為倒排索引衩藤,把文本處理成可被搜索的詞。分析器由一個(gè)分詞器(Tokenizer)和零個(gè)或多個(gè)標(biāo)記過(guò)濾器(TokenFilter)組成涛漂,也可以包含零個(gè)或多個(gè)字符過(guò)濾器(Character Filter)赏表。

在ElasticSearch引擎中,分析器的任務(wù)是分析(Analyze)文本數(shù)據(jù)匈仗,分析是分詞瓢剿,規(guī)范化文本的意思,其工作流程是:首先悠轩,字符過(guò)濾器對(duì)分析(analyzed)文本進(jìn)行過(guò)濾和處理跋选,例如從原始文本中移除HTML標(biāo)記,根據(jù)字符映射替換文本等哗蜈,過(guò)濾之后的文本被分詞器接收,分詞器把文本分割成標(biāo)記流坠韩,也就是一個(gè)接一個(gè)的標(biāo)記距潘,然后,標(biāo)記過(guò)濾器對(duì)標(biāo)記流進(jìn)行過(guò)濾處理只搁,例如音比,移除停用詞,把詞轉(zhuǎn)換成其詞干形式氢惋,把詞轉(zhuǎn)換成其同義詞等洞翩,最終稽犁,過(guò)濾之后的標(biāo)記流被存儲(chǔ)在倒排索引中;ElasticSearch引擎在收到用戶的查詢請(qǐng)求時(shí)骚亿,會(huì)使用分析器對(duì)查詢條件進(jìn)行分析已亥,根據(jù)分析的結(jié)構(gòu),重新構(gòu)造查詢来屠,以搜索倒排索引虑椎,完成全文搜索請(qǐng)求,可見(jiàn)俱笛,分析器扮演的是處理索引數(shù)據(jù)和查詢條件的重要角色捆姜。在2.4版本中,ElasticSearch?預(yù)定義了7個(gè)分析器迎膜,并且支持用戶根據(jù)預(yù)定義的字符過(guò)濾器泥技,分詞器和標(biāo)記過(guò)濾器創(chuàng)建自定義的分析器,以滿足用戶多樣性的文本分析需求磕仅。用戶在創(chuàng)建索引時(shí)配置索引的分析珊豹,通過(guò)向ElasticSearch發(fā)送請(qǐng)求,在請(qǐng)求body的settings 配置節(jié)中設(shè)置索引的分析器宽涌,例如平夜,為索引配置默認(rèn)的分析器:

我們也可自定義分詞如:

分詞發(fā)生在文檔創(chuàng)建和搜索的時(shí)候即讀時(shí)分詞和寫(xiě)時(shí)分詞,讀時(shí)分詞發(fā)生在用戶查詢時(shí)卸亮,ElasticSearch會(huì)即時(shí)地對(duì)用戶輸入的關(guān)鍵詞進(jìn)行分詞忽妒,分詞結(jié)果只存在內(nèi)存中,當(dāng)查詢結(jié)束時(shí)兼贸,分詞結(jié)果也會(huì)隨即消失段直。而寫(xiě)時(shí)分詞發(fā)生在文檔寫(xiě)入時(shí),ElasticSearch會(huì)對(duì)文檔進(jìn)行分詞后溶诞,將結(jié)果存入倒排索引鸯檬,該部分最終會(huì)以文件的形式存儲(chǔ)于磁盤(pán)上,不會(huì)因查詢結(jié)束或者 ElasticSearch重啟而丟失螺垢。ElasticSearch中處理分詞的部分被稱作分詞器喧务,英文是Analyzer,它決定了分詞的規(guī)則枉圃。ElasticSearch自帶了很多默認(rèn)的分詞器功茴,比如Standard、Keyword孽亲、Whitespace等等坎穿,默認(rèn)是Standard。當(dāng)我們?cè)谧x時(shí)或者寫(xiě)時(shí)分詞時(shí)可以指定要使用的分詞器。

發(fā)帖這部分代碼使用的是aop的環(huán)繞通知首先獲取索引的注解集合玲昧,EsIndexType索引類型栖茉,EsIndexs索引,然后獲取獲取索引的數(shù)據(jù)集合孵延,調(diào)用原方法保存相關(guān)數(shù)據(jù)至數(shù)據(jù)庫(kù)吕漂,最后組裝指定索引類型的數(shù)據(jù),保存索引

@Pointcut("@annotation(com.ibeetl.bbs.es.annotation.EsIndexType) || @annotation(com.ibeetl.bbs.es.annotation.EsIndexs)")

private void anyMethod(){//定義ES的切入點(diǎn)?

}

@Around("anyMethod()")public Object simpleAop(ProceedingJoinPoint pjp) throws Throwable{

?try {

? ? ?Signature sig = pjp.getSignature();

? ? ? MethodSignature msig = (MethodSignature) sig;//代理方法

? ? ? ?Object target = pjp.getTarget();//代理類

? ? ? ? Method method = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());

? ? ? ? ?EsIndexType esIndexType = method.getAnnotation(EsIndexType.class);

? ? ? ? ? EsIndexs esIndexs = method.getAnnotation(EsIndexs.class);

? ? ? ? ? ?//獲取索引的注解集合

? ? ? ? ? ? List types =new ArrayList<>();

? ? ? ? ? ? if(esIndexs !=null){

? ? ? ? ? ? ? ? ?types.addAll(Arrays.asList(esIndexs.value()));

? ? ? ? ? ? ?}

? ? ? ? ? ? ?if(esIndexType !=null){

? ? ? ? ? ? ? ? ? ?types.add(esIndexType);

? ? ? ? ? ? ? }

? ? ? ? ? ? ?//獲取索引的數(shù)據(jù)集合

? ? ? ? ? ? ? List typeDatas =new ArrayList<>();

? ? ? ? ? ? ? //當(dāng)操作為刪除時(shí)隙袁,需要提前獲取id

? ? ? ? ? ? ? ?for (EsIndexType index : types) {

????????????????????if(index.operateType() == EsOperateType.DELETE) {

? ? ? ? ? ? ? ? ? ? ? ? ? String key = index.key();

? ? ? ? ? ? ? ? ? ? ? ? ? ?Map parameterNames =this.getParameterNames(pjp);

? ? ? ? ? ? ? ? ? ? ? ? ? ? Integer id = (Integer)parameterNames.get(key);

? ? ? ? ? ? ? ? ? ? ? ? ? ? if(id ==null) {

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? logger.error(target.getClass().getName()+"$"+msig.getName()+":未獲取到主鍵痰娱,

????????????????????????????????無(wú)法更新索引");?

????????????????????????????}else {

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?BbsIndex bbsIndex =esService.createBbsIndex(index.entityType(), (Integer)id);

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //創(chuàng)建Elasticsearch索引,可根據(jù)自己的規(guī)則創(chuàng)建唯一索引菩收,這里的規(guī)則為:

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //bbs_post梨睁、bbs_reply、bbs_topic 這三張表的主鍵id拼接

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? String md5Id = EsUtil.getEsKey(bbsIndex.getTopicId(),bbsIndex.getPostId()

????????????????????????????????????????????????????????????????????????????????????,bbsIndex.getReplyId());

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? EsIndexTypeData data = new EsIndexTypeData(

????????????????????????????????????????????????????index.entityType(), index.operateType(), md5Id);

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? typeDatas.add(data);

? ? ? ? ? ????????????????????}

????????????????????????}

????????????????}

? ??????????????Object o = pjp.proceed();

? ? ? ? ? ? ? ? ?//當(dāng)操作為更新時(shí)娜饵,可以從返回值中獲取id

? ? ????????????? for (EsIndexType index : types) {

? ? ? ? ? ? ? ? ? ? ? if(index.operateType() != EsOperateType.DELETE) {

? ? ? ? ? ? ? ? ? ? ? ? ? ? Integer id =null;

? ? ? ? ? ? ? ? ? ? ? ? ? ? ?String key = index.key();

? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Map parameterNames =this.getParameterNames(pjp);

? ? ? ? ? ? ? ? ? ? ? ? ? ? ?id = (Integer)parameterNames.get(key);

? ? ? ? ? ? ? ? ? ? ? ? ? ? ?boolean resultErr =false;

? ? ? ? ? ????????????????????if(id ==null) {

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?if(oinstanceof ModelAndView) {

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ModelAndView modelAndView = (ModelAndView)o;

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?id = (Integer)modelAndView.getModel().get(key);

? ? ? ? ? ????????????????????????? }else if(oinstanceof JSONObject) {

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? JSONObject json = (JSONObject)o;

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? id = json.getInteger(key);

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? resultErr =1 == json.getInteger("err")?true:false;

? ? ? ? ? ????????????????????????? }

????????????????????????????}

????????????????????????????if(id ==null) {

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?if(!resultErr){

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? logger.error(target.getClass().getName()+"$"+msig.getName()+":未獲取到主鍵坡贺,

????????????????????????????????????????????????無(wú)法更新索引");

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?}

????????????????????????}else {

? ? ? ? ? ? ? ? ? ? ? ? ? ?EsIndexTypeData data =new EsIndexTypeData(index.entityType(), index.operateType(), id);

? ? ? ? ? ? ? ? ? ? ? ? ? ? ?typeDatas.add(data);

? ? ? ? ????????????????? }

????????????????????}

????????????????}

? ? ? ? ? ? ? ?for (EsIndexTypeData esIndexTypeData : typeDatas) {

? ? ? ? ? ? ? ? ? ?esService.editEsIndex(esIndexTypeData.getEntityType(), esIndexTypeData.getOperateType(),? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?esIndexTypeData.getId());

? ? ????????? }

????????????return o;

? ? ?}catch (Exception e) {

????????????throw e;

? ? ?}

}

BbsIndexRepository類是? extends?? CrudRepository<T,ID>, 而CrudRepository 的實(shí)現(xiàn)AbstractElasticsearchRepository中的save(),當(dāng)我們?cè)L問(wèn)配置文件中配置的Elasticsearch中的索引地址如下就會(huì)有內(nèi)容了箱舞。

現(xiàn)在Elasticsearch中已經(jīng)存儲(chǔ)了一些數(shù)據(jù)遍坟,我們可以根據(jù)業(yè)務(wù)需求開(kāi)始進(jìn)行搜索實(shí)踐了

2.4、論壇首頁(yè)select模塊

如果不是通過(guò)關(guān)鍵字搜索則僅僅走數(shù)據(jù)庫(kù)查詢晴股,如果是通過(guò)關(guān)鍵字查詢則先查詢索引即先查詢Elasticsearch所存儲(chǔ)的文襠值然后再通過(guò)相關(guān)對(duì)應(yīng)值查詢數(shù)據(jù)庫(kù)愿伴。這里只分析下通過(guò)關(guān)鍵字搜索即索引查詢。對(duì)PageQuery searcherKeywordPage =this.esService.getQueryPage(keyword,p);的getQueryPage()方法進(jìn)行分析电湘。當(dāng)我們檢索一個(gè)關(guān)鍵字帖子時(shí)我們只要執(zhí)行HTTP GET請(qǐng)求并指出文檔的“地址”——索引隔节、類型和ID既可。根據(jù)這三部分信息寂呛,我們就可以返回原始JSON文檔:

try {

????????HttpHeaders headers =new HttpHeaders();

? ? ? ? ?headers.setContentType(MediaType.APPLICATION_JSON);

? ? ? ? ?//渲染模板,bssContent.html為模板

? ? ? ? ?Template template =beetlTemplate.getTemplate("/bssContent.html");

? ? ? ? ? template.binding("pageSize", pageSize);

? ? ? ? ? template.binding("pageNumber", pageNumber);

? ? ? ? ? template.binding("keyword", keyword);

? ? ? ? ? String esJson = template.render();

? ? ? ? ? HttpEntity? httpEntity =new HttpEntity(esJson,headers);

? ? ? ? ?//使用restTemplate.postForObject()發(fā)送請(qǐng)求獲取本地Elasticsearch服務(wù)上的值怎诫;

? ? ? ? ?// elasticsearch.bbs.content.url為配置文件中配置的Elasticsearch bbs索引地址

? ? ? ? ? String result =restTemplate.postForObject(env.getProperty("elasticsearch.bbs.content.url"),?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? httpEntity, String.class);

? ? ? ? ? List indexObjectList =new ArrayList<>();

? ? ? ? ?//使用ObjectMapper對(duì)含有任意key的JSON進(jìn)行反序列化將多個(gè)實(shí)體放到Map中

? ? ? ? JsonNode root =new ObjectMapper().readTree(result);

? ? ? ? //獲取Elasticsearch服務(wù)上的值的總條數(shù)

? ? ? ? long total = root.get("hits").get("total").asLong();

? ? ? ? //獲取Elasticsearch服務(wù)上的值文本list

? ? ? ? Iterator iterator = root.get("hits").get("hits").iterator();

? ? ? ? //循環(huán)iterator

? ? ? ?while(iterator.hasNext()) {

? ? ? ? ? ?JsonNode jsonNode = iterator.next();

? ? ? ? ? ?double score = jsonNode.get("_score").asDouble();

? ? ? ? ? ? //循環(huán)獲取值并序列化為BbsIndex

? ? ? ? ? ? BbsIndex index =new ObjectMapper().convertValue(jsonNode.get("_source"), BbsIndex.class);

? ? ? ? ? ? index.setContent(jsonNode.get("highlight").get("content").get(0).asText());

? ? ? ? ? ?//獲取數(shù)據(jù)庫(kù)相關(guān)信息并set到indexObject對(duì)象中

? ? ? ? ? if(index.getTopicId() !=null) {

????????????????IndexObject indexObject =null;

? ? ? ????????? BbsTopic topic =bbsService.getTopic(index.getTopicId());

? ? ? ????????? BbsUser user = topic.getUser();

? ? ? ? ? ? ? ?BbsModule module = topic.getModule();

? ? ? ? ? ? ? ? ? .......................................省略其他

最后通過(guò)ModelAndView渲染到頁(yè)面

?2.5贷痪、帖子刪除

帖子刪除同樣用到了spring AOP環(huán)繞通知技術(shù)幻妓,這里AOP底層同樣是cjlib動(dòng)態(tài)代理,通過(guò)掃描注解獲取索引的注解集合劫拢,通過(guò)id從數(shù)據(jù)庫(kù)查早相應(yīng)信息后用發(fā)帖時(shí)相同的規(guī)則MD5加密肉津,刪除數(shù)據(jù)庫(kù)bbs_topic、bbs_reply舱沧、bbs_post表的相關(guān)數(shù)據(jù)妹沙,最后通過(guò)id刪除Elasticsearch服務(wù)上存儲(chǔ)的值



通過(guò)索引刪除Elasticsearch對(duì)應(yīng)的數(shù)據(jù)


2.6、回復(fù)帖子狗唉、置頂、精華模塊

回復(fù)帖子置頂和設(shè)置精華功能模塊實(shí)現(xiàn)與發(fā)帖和刪帖類似涡真,同樣是spring AOP環(huán)繞通知分俯,回帖對(duì)bbs_topic表的post_count(回帖總數(shù))進(jìn)行統(tǒng)計(jì)加以肾筐,用戶和帖子關(guān)聯(lián)表bbs_message對(duì)應(yīng)數(shù)據(jù)插入,內(nèi)容表bbs_post插入回帖數(shù)據(jù)同時(shí)更改用戶表的等級(jí)等相關(guān)信息缸剪,Elasticsearch服務(wù)上對(duì)應(yīng)索引的值也做相應(yīng)更改

2.7吗铐、注冊(cè)、退出

注冊(cè)時(shí)password使用MD5加密杏节,退出移除session

以上就是整個(gè)bbs論壇系統(tǒng)后端的分析唬渗,前端部分很基礎(chǔ),所有的請(qǐng)求都是ajax請(qǐng)求

三奋渔、學(xué)習(xí)目的

1镊逝、在分布式系統(tǒng)尤其一些電商核心業(yè)務(wù)交易系統(tǒng)的設(shè)計(jì)都需要實(shí)時(shí)大數(shù)據(jù)搜索分析,比如嫉鲸,商品中心的上千萬(wàn)的數(shù)據(jù)需要實(shí)時(shí)搜索撑蒜,再到海量的在線訂單實(shí)時(shí)查詢都需要用到搜索,在一些DevOps的工具中都需要提供強(qiáng)大的實(shí)時(shí)搜索功能玄渗,elasticsearch就是一套很好的解決方案它可以幫助你用前所未有的速度去處理大規(guī)模數(shù)據(jù)座菠。

2、beetl模板也是我們?cè)趙eb開(kāi)發(fā)中經(jīng)常使用的藤树,他可生成特定格式的文檔使用戶界面與業(yè)務(wù)數(shù)據(jù)分離浴滴。

四、通過(guò)對(duì)這個(gè)簡(jiǎn)易論壇項(xiàng)目的學(xué)習(xí)分析岁钓,我覺(jué)得這個(gè)項(xiàng)目是我們學(xué)習(xí)elasticsearch升略、beetl和beetlSQL的一個(gè)很好的demo,?在使用Elasticsearch時(shí)創(chuàng)建Elasticsearch索引是我們需要注意的地方: “?可以將磁盤(pán)里的東西盡量搬進(jìn)內(nèi)存甜紫,減少磁盤(pán)隨機(jī)讀取次數(shù)(同時(shí)也利用磁盤(pán)順序讀特性)降宅,結(jié)合壓縮算法,用及其苛刻的態(tài)度使用內(nèi)存”囚霸,?而不需要索引的字段腰根,一定要明確定義出來(lái),因?yàn)槟J(rèn)是自動(dòng)建索引的拓型,同樣额嘿,對(duì)于String類型的字段,不需要analysis的也需要明確定義出來(lái)劣挫,因?yàn)槟J(rèn)也是會(huì)analysis的册养,選擇有規(guī)律的ID很重要,隨機(jī)性太大的ID(比如java的UUID)不利于查詢压固。當(dāng)然其中也感覺(jué)也有需要擴(kuò)展補(bǔ)充的地方球拦,比如在安全方面、并發(fā)請(qǐng)求、elasticsearch和DB數(shù)據(jù)一致性的處理機(jī)制(目前這個(gè)項(xiàng)目只能手動(dòng)刪除數(shù)據(jù)保證elasticsearch 和DB數(shù)據(jù)的一致)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末坎炼,一起剝皮案震驚了整個(gè)濱河市愧膀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌谣光,老刑警劉巖檩淋,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異萄金,居然都是意外死亡蟀悦,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門氧敢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)日戈,“玉大人,你說(shuō)我怎么就攤上這事福稳∠牙” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵的圆,是天一觀的道長(zhǎng)鼓拧。 經(jīng)常有香客問(wèn)我,道長(zhǎng)越妈,這世上最難降的妖魔是什么季俩? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮梅掠,結(jié)果婚禮上酌住,老公的妹妹穿的比我還像新娘。我一直安慰自己阎抒,他們只是感情好酪我,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著且叁,像睡著了一般都哭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上逞带,一...
    開(kāi)封第一講書(shū)人閱讀 50,084評(píng)論 1 291
  • 那天欺矫,我揣著相機(jī)與錄音,去河邊找鬼展氓。 笑死穆趴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的遇汞。 我是一名探鬼主播未妹,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼簿废,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了络它?” 一聲冷哼從身側(cè)響起捏鱼,我...
    開(kāi)封第一講書(shū)人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎酪耕,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體轨淌,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡迂烁,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了递鹉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盟步。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖躏结,靈堂內(nèi)的尸體忽然破棺而出却盘,到底是詐尸還是另有隱情,我是刑警寧澤媳拴,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布黄橘,位于F島的核電站,受9級(jí)特大地震影響屈溉,放射性物質(zhì)發(fā)生泄漏塞关。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一子巾、第九天 我趴在偏房一處隱蔽的房頂上張望帆赢。 院中可真熱鬧,春花似錦线梗、人聲如沸椰于。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)瘾婿。三九已至,卻和暖如春僻造,著一層夾襖步出監(jiān)牢的瞬間憋他,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工髓削, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留竹挡,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓立膛,卻偏偏與公主長(zhǎng)得像揪罕,于是被迫代替她去往敵國(guó)和親梯码。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容