您好,登錄后才能下訂單哦!
前一陣子準備為項目搭建一個簡單的搜索服務,雖然業務數據庫mongodb提供了文本搜索的支持,但是在大量文檔需要通過關鍵詞進行定位時,es明顯更加適合去作為一個搜索引擎(雖然我們之前大部分使用到了ELK那套分析和可視化的特性)。Elasticsearch建立在Lucene之上并且支持極其快速的查詢和豐富的查詢語法,偶爾也可以作為一個輕量級的NoSQL。但是對復雜查詢和聚合操作的能力并不是很強。
本篇不會提及如何搭建一個簡單搜索服務,而是記錄一下大約一周工作時間內遇見的幾個坑。。
為什么選擇elasticsearch 5.x?
新服務沒有任何歷史包袱,理論上應該用最新的6.x,然而spring-data-elasticsearch只支持到的5.x,時間緊也無法很好直接封裝一層api,也是因為ELK那套東西之前版本混亂,無奈es從2.x直接到了5.x。查詢一下5.x和2.x的差別,簡單說就是磁盤空間-50%,索引時間-50%,查詢性能+25%。
由于spring-data-elasticsearch必須升級到3.0.7,導致spring必須升級到2.x,也直接導致了后面踩到的坑。
docker安裝es會默認安裝x-path plugin
雖然spring-data支持es5.x,但是功能并不非常完善,因此如果安裝了x-path插件,需要引入org.elasticsearch.client:x-pack-transport:5.5.0,版本必須和es版本一致,并且自己實現TransportClient,如下
@Component public class ESconfig { @Bean public TransportClient transportClient() throws UnknownHostException { TransportClient client = new PreBuiltXPackTransportClient(Settings.builder() .put("cluster.name", "docker-cluster") .put("xpack.security.user", "elastic:changeme") .build()) .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("0.0.0.0"), 9300)); return client; } }
這也是因為不想再到docker里去處理x-path這個插件而選擇的一個比較快捷的解決方案,沒必要的情況下,暫時也不用接觸到es本身的一些東西。
mq會保存message的class信息導致deserialized失敗
一直沒有提到標題中的rabbitmq,因為只是單純的用它作為一個消息隊列,當數據發生變化時,將消息id丟入mq,由search服務這邊的consumer去消費。
問題就是在消息丟入mq時,封裝成了一個自己的Object, 導致使用rabbitTemplate.receiveAndConvert時失敗,因為message會帶著Object的package信息。無奈之下,consumer只能直接獲取queue里的message bytes, 用ObjectMapper.readValue的方法將json形式轉換成一個Object。
gradle配置可以使用-Dloader.main指定啟動函數
正是因為引入了mq,所以search服務需要啟動一個consumer,用的方法是另外實現一個不啟動Web服務的Application,并且配置一個SimpleMessageListenerContainer和MessageListenerAdapter如下:
@Bean SimpleMessageListenerContainer container(ConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter, MQconfig properties) { SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(); container.setConnectionFactory(connectionFactory); container.setQueueNames(properties.getQueueName()); container.setMessageListener(listenerAdapter); return container; } @Bean MessageListenerAdapter listenerAdapter() { MessageListenerAdapter listenerAdapter = new MessageListenerAdapter(itemConsumer, "consume"); return listenerAdapter; }
問題在于gradle配置的時候,找了很久如何使得build出來的jar包可以指定-Dloader.main指定啟動Application,解決方法如下:
在xxx.gradle文件里添加
bootJar { manifest { attributes 'Main-Class': 'org.springframework.boot.loader.PropertiesLauncher' } }
在springboot 1.5.9的項目里,需要指定啟動Application,需要添加
springBoot{ layout = "ZIP" }
查看是否生效的辦法是build以后 直接解壓jar包,在xxx(項目名)/META-INFO/MANIFEST.MF里查看,如果
Main-Class: org.springframework.boot.loader.PropertiesLauncher
則正確,如果
Main-Class: org.springframework.boot.loader.JarLauncher
則依舊會啟動文件里的Start-Class
es無法修改Index的mapping
由于只是單純使用了es的文本檢索功能,導致實際應用時有許多搜索結果不盡如人意的地方,比如搜索“桌子”, 無法搜索到 “電腦桌/辦公桌”等xx桌內容,這樣的情況還有很多。 因此加入了synonym dictionary,在需要分詞的字段上不使用本身的ik_smart分詞器,這樣某些字段的mapping需要改為
// analyzer是自己的分詞器名字 @Field(type = FieldType.Text, index = true, analyzer = "synonym") private String description;
由于es的mapping無法修改,只能通過手動創建一個新的mapping,再通過reIndex方法去backfill數據(es5.x自帶了reIndex 的api)。網上有通過alias的方法,在某些修改場景下,不需要重新啟動/部署應用就可以平滑的修改mapping,具體可以查詢了解一下。
以上差不多搭建一個搜索服務踩到的一些坑,有幾個消耗了大量時間和精力去解決,在此列出來希望希望有借鑒意義。之后搜索服務有優化的地方,還會繼續慢慢更新,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。