Broadleaf項目中關於商品的搜索使用了嵌入式的Solr服務器,這個從配置文件中能夠看出來。java
項目主頁: http://www.broadleafcommerce.com/git
示例網站: http://demo.broadleafcommerce.org/github
示例網站源代碼: https://github.com/BroadleafCommerce/DemoSiteweb
從示例網站源代碼的applicationContext.xml文件中能夠看到關於solr的配置:spring
<bean id="solrEmbedded" class="java.lang.String"> <constructor-arg value="solrhome"/> </bean> <bean id="blSearchService" class="org.broadleafcommerce.core.search.service.solr.SolrSearchServiceImpl"> <constructor-arg name="solrServer" ref="${solr.source}" /> <constructor-arg name="reindexServer" ref="${solr.source.reindex}" /> </bean> <bean id="rebuildIndexJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="blSearchService" /> <property name="targetMethod" value="rebuildIndex" /> </bean> <bean id="rebuildIndexTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean"> <property name="jobDetail" ref="rebuildIndexJobDetail" /> <property name="startDelay" value="${solr.index.start.delay}" /> <property name="repeatInterval" value="${solr.index.repeat.interval}" /> </bean> <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="rebuildIndexTrigger" /> <!--<ref bean="purgeCartTrigger" />--> <!--<ref bean="purgeCustomerTrigger" />--> </list> </property> </bean>
資源配置文件在common.properties:shell
web.defaultPageSize=15 web.maxPageSize=100 solr.source=solrEmbedded solr.source.reindex=solrEmbedded solr.index.start.delay=5000 solr.index.repeat.interval=3600000
從上能夠看出使用的Solr是嵌入式服務,Solr配置文件(schema.xml和solrconfig.xml)在 https://github.com/BroadleafCommerce/DemoSite/tree/master/site/src/main/resources 目錄下。數據庫
從源代碼SolrSearchServiceImpl.java中能夠看出,一共啓動了兩個Solr服務,分別對應primary和reindex兩個solrcore,primary用於查詢,reindex用於重建索引。apache
本篇文章只是將嵌入式Solr服務換成獨立運行的Solr服務,你還能夠更進一步換成SolrCloud集羣。json
單獨搭建搜素引擎服務器緩存
支持增量更新索引
支持手動重建索引
1.修改原系統中的嵌入式搜索引擎爲獨立部署的搜索引擎,安裝方法見下文。
2.擴展原系統中的SolrSearchServiceImpl類,添加增長索引的方法。
3.修改原來系統中商品的service類(我這裏調用的是LLCatalogServiceImpl,該類是新添加的),在saveProduct方法中添加往搜索引擎添加索引的方法。
4.修改原系統中solr相關的配置文件。
5.修改原系統中的重建索引的定時任務,以支持手動重建索引。
一、搭建獨立運行的solr服務器
你能夠參考:Apache Solr介紹及安裝
關鍵在於solr/home的定義,以及在該目錄下建立兩個目錄,分別爲primary和reindex,兩個目錄下的配置文件都同樣,solr/home目錄結構以下:
➜ solrhome-solr tree -L 3 . ├── primary │ └── conf │ ├── schema.xml │ └── solrconfig.xml ├── reindex │ └── conf │ ├── schema.xml │ └── solrconfig.xml └── solr.xml 4 directories, 5 files
solr.xml文件內容以下:
<?xml version="1.0" encoding="UTF-8" ?> <solr persistent="true"> <cores defaultCoreName="primary" adminPath="/admin/cores"> <core instanceDir="reindex" name="reindex"/> <core instanceDir="primary" name="primary"/> </cores> </solr>
schema.xml內容和原來的基本同樣,只是添加了一行:
<field name="_version_" type="long" indexed="true" stored="true" multiValued="false"/>
solrconfig.xml內容以下:
<?xml version="1.0" encoding="UTF-8" ?> <config> <luceneMatchVersion>4.4</luceneMatchVersion> <directoryFactory name="DirectoryFactory" class="${solr.directoryFactory:solr.StandardDirectoryFactory}"/> <schemaFactory class="ClassicIndexSchemaFactory"/> <updateHandler class="solr.DirectUpdateHandler2"> <autoCommit> <maxDocs>2</maxDocs> <maxTime>3000</maxTime> </autoCommit> </updateHandler> <requestHandler name="/get" class="solr.RealTimeGetHandler"> <lst name="defaults"> <str name="omitHeader">true</str> </lst> </requestHandler> <requestHandler name="/replication" class="solr.ReplicationHandler" startup="lazy" /> <requestDispatcher handleSelect="true" > <requestParsers enableRemoteStreaming="false" multipartUploadLimitInKB="2048" formdataUploadLimitInKB="2048" /> <httpCaching never304="true" /> </requestDispatcher> <requestHandler name="standard" class="solr.StandardRequestHandler" default="true" /> <requestHandler name="/analysis/field" startup="lazy" class="solr.FieldAnalysisRequestHandler" /> <requestHandler name="/update" class="solr.UpdateRequestHandler" /> <requestHandler name="/update/csv" class="solr.CSVRequestHandler" startup="lazy" /> <requestHandler name="/update/json" class="solr.JsonUpdateRequestHandler" startup="lazy" /> <requestHandler name="/admin/" class="org.apache.solr.handler.admin.AdminHandlers" /> <requestHandler name="/admin/ping" class="solr.PingRequestHandler"> <lst name="invariants"> <str name="q">solrpingquery</str> </lst> <lst name="defaults"> <str name="echoParams">all</str> </lst> </requestHandler> <queryResponseWriter name="json" class="solr.JSONResponseWriter"> <str name="content-type">text/plain; charset=UTF-8</str> </queryResponseWriter> <!-- config for the admin interface --> <admin> <defaultQuery>solr</defaultQuery> </admin> </config>
二、擴展SolrSearchServiceImpl類
在core模塊建立org.broadleafcommerce.core.search.service.solr.ExtSolrSearchService接口,該接口定義以下:
package org.broadleafcommerce.core.search.service.solr; import java.io.IOException; import org.broadleafcommerce.common.exception.ServiceException; import org.broadleafcommerce.core.catalog.domain.Product; import org.broadleafcommerce.core.search.service.SearchService; public interface ExtSolrSearchService extends SearchService { public void addProductIndex(Product product) throws ServiceException, IOException; }
而後,建立其實現類org.broadleafcommerce.core.search.service.solr.ExtSolrSearchServiceImpl,該實現類定義以下:
package org.broadleafcommerce.core.search.service.solr; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import javax.annotation.Resource; import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.solr.client.solrj.SolrServer; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.SolrInputDocument; import org.broadleafcommerce.common.exception.ServiceException; import org.broadleafcommerce.common.locale.domain.Locale; import org.broadleafcommerce.common.util.StopWatch; import org.broadleafcommerce.common.util.TransactionUtils; import org.broadleafcommerce.core.catalog.domain.Product; import org.broadleafcommerce.core.search.domain.Field; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; import org.xml.sax.SAXException; public class ExtSolrSearchServiceImpl extends SolrSearchServiceImpl implements ExtSolrSearchService { private static final Log LOG = LogFactory .getLog(ExtSolrSearchServiceImpl.class); @Resource(name = "blSolrIndexService") protected SolrIndexServiceImpl solrIndexServiceImpl; public ExtSolrSearchServiceImpl(SolrServer solrServer, SolrServer reindexServer) { super(solrServer, reindexServer); } public ExtSolrSearchServiceImpl(SolrServer solrServer) { super(solrServer); } public ExtSolrSearchServiceImpl(String solrServer, String reindexServer) throws IOException, ParserConfigurationException, SAXException { super(solrServer, reindexServer); } public ExtSolrSearchServiceImpl(String solrServer) throws IOException, ParserConfigurationException, SAXException { super(solrServer); } public void addProductIndex(Product product) throws ServiceException, IOException { TransactionStatus status = TransactionUtils.createTransaction( "saveProduct", TransactionDefinition.PROPAGATION_REQUIRED, solrIndexServiceImpl.transactionManager, true); StopWatch s = new StopWatch(); try { List<Field> fields = fieldDao.readAllProductFields(); List<Locale> locales = solrIndexServiceImpl.getAllLocales(); SolrInputDocument document = solrIndexServiceImpl.buildDocument( product, fields, locales); if (LOG.isTraceEnabled()) { LOG.trace(document); } SolrContext.getServer().add(document); SolrContext.getServer().commit(); TransactionUtils.finalizeTransaction(status, solrIndexServiceImpl.transactionManager, false); } catch (SolrServerException e) { TransactionUtils.finalizeTransaction(status, solrIndexServiceImpl.transactionManager, true); throw new ServiceException("Could not rebuild index", e); } catch (IOException e) { TransactionUtils.finalizeTransaction(status, solrIndexServiceImpl.transactionManager, true); throw new ServiceException("Could not rebuild index", e); } catch (RuntimeException e) { TransactionUtils.finalizeTransaction(status, solrIndexServiceImpl.transactionManager, true); throw e; } LOG.info(String.format("Finished adding index in %s", s.toLapString())); } protected List<Product> getProducts(QueryResponse response) { final List<Long> productIds = new ArrayList<Long>(); SolrDocumentList docs = response.getResults(); for (SolrDocument doc : docs) { productIds .add((Long) doc.getFieldValue(shs.getProductIdFieldName())); } /** * TODO 請添加緩存相關代碼 */ List<Product> products = productDao.readProductsByIds(productIds); // We have to sort the products list by the order of the productIds list // to maintain sortability in the UI if (products != null) { Collections.sort(products, new Comparator<Product>() { public int compare(Product o1, Product o2) { return new Integer(productIds.indexOf(o1.getId())) .compareTo(productIds.indexOf(o2.getId())); } }); } return products; } }
三、 修改solr相關配置文件
a. 刪除web模塊中/web/src/main/webapp/WEB-INF/applicationContext.xml的如下代碼:
<bean id="blSearchService" class="org.broadleafcommerce.core.search.service.solr.SolrSearchServiceImpl"> <constructor-arg name="solrServer" ref="${solr.source}" /> <constructor-arg name="reindexServer" ref="${solr.source.reindex}" /> </bean>
b.刪除web模塊中web/src/main/resources/runtime-properties/common.properties的如下代碼:
solr.source=solrEmbedded solr.source.reindex=solrEmbedded
c. 在core模塊中core/src/main/resources/applicationContext.xml添加以下代碼:
<bean id="solrServer" class="org.apache.solr.client.solrj.impl.HttpSolrServer"> <constructor-arg value="${solr.url}" /> </bean> <bean id="solrReindexServer" class="org.apache.solr.client.solrj.impl.HttpSolrServer"> <constructor-arg value="${solr.url.reindex}" /> </bean> <bean id="blSearchService" class="org.broadleafcommerce.core.search.service.solr.ExtSolrSearchServiceImpl"> <constructor-arg name="solrServer" ref="${solr.source}" /> <constructor-arg name="reindexServer" ref="${solr.source.reindex}" /> </bean>
d. 在core模塊中core/src/main/resources/runtime-properties/common-shared.properties添加以下代碼:
solr.url= solr.url.reindex=http://localhost:8080/solr/reindex solr.source=solrServer solr.source.reindex=solrReindexServer
四、 修改LLCatalogServiceImpl類
添加以下代碼:
@Resource(name = "blSearchService") private ExtSolrSearchService extSolrSearchService;
修改該類的saveProduct方法以下:
@Override @Transactional("blTransactionManager") public Product saveProduct(Product product) { Product dbProduct = catalogService.saveProduct(product); try { extSolrSearchService.addProductIndex(dbProduct); } catch (ServiceException e) { e.printStackTrace(); throw new RuntimeException(e); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } return dbProduct; }
五、 修改定時任務
a. web系統啓動時候,會查詢數據庫中商品,而後重建索引。該功能在applicationContext.xml中已經定義了定時任務,建議取消該定時任務。去掉如下代碼:
<bean id="rebuildIndexJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="blSearchService" /> <property name="targetMethod" value="rebuildIndex" /> </bean> <bean id="rebuildIndexTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean"> <property name="jobDetail" ref="rebuildIndexJobDetail" /> <property name="startDelay" value="${solr.index.start.delay}" /> <property name="repeatInterval" value="${solr.index.repeat.interval}" /> </bean> <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="rebuildIndexTrigger" /> </list> </property> </bean>
b. 編寫main方法,打成jar包,而後編寫shell腳本,用於手動重建索引或者設置定時任務。該類須要獲取一個名稱爲blSearchService的bean,而後調用該bean的rebuildIndex方法,主要代碼以下。
@Resource(name = "blSearchService") private SearchService extSolrSearchService; public void doRebuild(){ extSolrSearchService.rebuildIndex(); }