SolrCloud(solr 云)是Solr提供的分布式搜索方案,当你需要大规模,容错,分布式索引和检索能力时使用 SolrCloud。当一个系统的索引数据量少的时候是不需要使用SolrCloud的,当索引量很大,搜索请求并发很高,这时需要使用SolrCloud来满足这些需求。
SolrCloud是基于Solr和Zookeeper的分布式搜索方案,它的主要思想是使用Zookeeper作为集群的配置信息中心。也就是管理集群的.
它有几个特色功能:
1)集中式的配置信息
2)自动容错
3)近实时搜索
4)查询时自动负载均衡
Solr集群的系统架构
如下图,上半部分是实现的过程,下半部分是原理,看下半部分,就是把所有的数据交给collection来管理,它下面可以分无数个分片,每个分片下可以有无数个节点,这些节点存储具体的数据,每个片下的节点存储的数据是相同的,一个主节点两个副节点,当并发量高的时候副节点也可以使用,也就是当数据存储不下的时候可以加分片,当并发量提升的时候,加节点.节点就相当于上半图的core索引库,分片就相当于solr服务器.
这个图中的collection不是solr单机版中的核,每个分片才是每一个核,把每个核都集中到一块就是集群
搭建solr集群,因为没那么多服务器,所以搭建一个伪集群,在一台服务器上实现,搭建前先搭建好一台solr服务器https://blog.csdn.net/kxj19980524/article/details/85238989我下面写的是直接在一台的基础上搭建的.
首先搭建一个zookeeper集群https://blog.csdn.net/kxj19980524/article/details/85528403
然后开始搭建solr集群,因为每个solr都是在一个tomcat上运行的,所以需要四个tomcat,复制四份tomcat到solr-cloud下
然后复制单机版的solr分别放到每个tomcat的webapps下
然后进入tomcat01/conf/server.xml下改这三个端口号,tomcat02,和03,04也都改了,只要改成不同的就可以了.
tomcat02
tomcat03
04我就不截了,改好后把solrhome复制四份
然后进入到第一个tomcat的web.xml改成相应的solrhome的位置,把四个都改成相应的位置,我就不一一截图了.
然后进入第一个solrhome中修改solr.xml,把ip地址和端口改成想对应的.这就演示一个把其余三个都改了
然后建立zookeeper集群和solr集群的连接,进入tomcat01下的bin下的catalina.sh编辑它
JAVA_OPTS="-DzkHost=192.168.25.155:2181,192.168.25.155:2182,192.168.25.155:2183"
在这个地方加一行配置,端口号和ip改成zookeeper集群的端口ip就可以了,然后把其余三个tomcat也都加上同样的配置就可以了.![](https://i-blog.csdnimg.cn/blog_migrate/ca5eb8de95bc0d3607084fef2d02409b.png)
配置好,因为现在是集群了,但是每个solrhome下都有自己独自的配置域的配置文件,现在需要上传给zookeeper统一管理.使用zookeeper的客户端进行上传,客户端在解压缩的solr下面.
执行下面这个命令进行上传,把ip和端口改成zookeeper的ip端口
./zkcli.sh -zkhost 192.168.25.155:2181,192.168.25.155:2182,192.168.25.155:2183 -cmd upconfig -confdir /usr/local/solr-cloud/solrhome01/collection1/conf -confname myconf
执行完后查看一下是否上传成功了,进入到zookeeper下的客户端进行查看,就会看到上传成功的文件了,默认连接的是2181端口的zookeeper,也可以使用命令./zkCli.sh -server 192.168.25.155:2182来指定zookeeper的端口进行访问.
然后输入quit命令退出客户端,启动所有tomcat,编写脚本,然后给它权限,并启动
/usr/local/solr-cloud/tomcat01/bin/startup.sh
/usr/local/solr-cloud/tomcat02/bin/startup.sh
/usr/local/solr-cloud/tomcat03/bin/startup.sh
/usr/local/solr-cloud/tomcat04/bin/startup.sh
然后分别查看四个tomcat的启动状况看看是否启动成功
tail -f /usr/local/solr-cloud/tomcat01/logs/catalina.out
然后关闭防火墙,进行访问solr
现在只有一个索引库并且是一片,一个主节点三个备份节点,现在只能做负载均衡实现高并发,并不能实现扩大索引库的效果,所以需要执行命令,这个命令之间在浏览器路径执行就可以了,这个命令的意思是,创建一个controller2索引库,numShards的意思是分为两片replicationFactor的意思是每片两个节点一主一备,把ip改为自己的就可以了
执行完后再刷新solr页面,就会发现建好了![](https://i-blog.csdnimg.cn/blog_migrate/b06a7103bb06c90180fdddb9454f1571.png)
执行这个命令还可以把不用的controller1删除掉
http://192.168.25.155:8180/solr/admin/collections?action=DELETE&name=collection1
这个集群的整个搭建过程就完成了
接下来使用solrj管理solrCloud集群 ,在这之前先看一下单机版的Solrj管理方式https://blog.csdn.net/kxj19980524/article/details/85202324
导哪些包就不截图了在单机版都有,直接上代码了
package cn.e3mall.solrj;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.impl.CloudSolrServer;
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.junit.Test;
public class TestSolrCloud {
@Test
public void testAddDocument() throws Exception {
//创建一个集群的连接,应该使用CloudSolrServer创建。
//zkHost:zookeeper的地址列表 参数是zookeeper的ip地址
CloudSolrServer solrServer = new CloudSolrServer("192.168.25.163:2181,192.168.25.163:2182,192.168.25.163:2183");
//设置一个defaultCollection属性。
solrServer.setDefaultCollection("collection2");
//创建一个文档对象
SolrInputDocument document = new SolrInputDocument();
//向文档中添加域
document.setField("id", "solrcloud01");
document.setField("item_title", "测试商品01");
document.setField("item_price", 123);
//把文件写入索引库
solrServer.add(document);
//提交
solrServer.commit();
}
@Test
public void testQueryDocument() throws Exception {
//创建一个CloudSolrServer对象
CloudSolrServer cloudSolrServer = new CloudSolrServer("192.168.25.163:2181,192.168.25.163:2182,192.168.25.163:2183");
//设置默认的Collection
cloudSolrServer.setDefaultCollection("collection2");
//创建一个查询对象
SolrQuery query = new SolrQuery();
//设置查询条件
query.setQuery("*:*");
//执行查询
QueryResponse queryResponse = cloudSolrServer.query(query);
//取查询结果
SolrDocumentList solrDocumentList = queryResponse.getResults();
System.out.println("总记录数:" + solrDocumentList.getNumFound());
//打印
for (SolrDocument solrDocument : solrDocumentList) {
System.out.println(solrDocument.get("id"));
System.out.println(solrDocument.get("title"));
System.out.println(solrDocument.get("item_title"));
System.out.println(solrDocument.get("item_price"));
}
}
}
上面这个是测试demo,在项目中使用的话参考https://blog.csdn.net/kxj19980524/article/details/85238989
这个里面是注入的单击版的,现在使用集群版的话别的不用改,只要把注入的对象换成集群的对象就可以了
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd">
<!-- 单机版solrJ -->
<!-- <bean id="httpSolrServer" class="org.apache.solr.client.solrj.impl.HttpSolrServer">
<constructor-arg index="0" value="http://192.168.25.163:8080/solr/collection1"/>
</bean> -->
<!-- 集群版solrJ -->
<bean id="cloudSolrServer" class="org.apache.solr.client.solrj.impl.CloudSolrServer">
<constructor-arg index="0" value="192.168.25.163:2181,192.168.25.163:2182,192.168.25.163:2183"></constructor-arg>
<property name="defaultCollection" value="collection2"></property>
</bean>
</beans>
搜索框调用的方法根据搜索内容返回相对应的信息
package cn.e3mall.search.dao;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.web.bind.annotation.ResponseBody;
import cn.e3mall.common.pojo.SearchItem;
import cn.e3mall.common.pojo.SearchResult;
/**
* 商品搜索dao
* <p>Title: SearchDao</p>
* <p>Description: </p>
* <p>Company: www.itcast.cn</p>
* @version 1.0
*/
@Repository
public class SearchDao {
@Autowired
private SolrServer solrServer;
/**
*根据查询条件查询索引库
* <p>Title: search</p>
* <p>Description: </p>
* @param query
* @return
*/
public SearchResult search(SolrQuery query) throws Exception {
//根据query查询索引库
QueryResponse queryResponse = solrServer.query(query);
//取查询结果。
SolrDocumentList solrDocumentList = queryResponse.getResults();
//取查询结果总记录数
long numFound = solrDocumentList.getNumFound();
SearchResult result = new SearchResult();
result.setRecordCount(numFound);
//取商品列表,需要取高亮显示
Map<String, Map<String, List<String>>> highlighting = queryResponse.getHighlighting();
List<SearchItem> itemList = new ArrayList<>();
for (SolrDocument solrDocument : solrDocumentList) {
SearchItem item = new SearchItem();
item.setId((String) solrDocument.get("id"));
item.setCategory_name((String) solrDocument.get("item_category_name"));
item.setImage((String) solrDocument.get("item_image"));
item.setPrice((long) solrDocument.get("item_price"));
item.setSell_point((String) solrDocument.get("item_sell_point"));
//取高亮显示
List<String> list = highlighting.get(solrDocument.get("id")).get("item_title");
String title = "";
if (list != null && list.size() > 0) {
title = list.get(0);
} else {
title = (String) solrDocument.get("item_title");
}
item.setTitle(title);
//添加到商品列表
itemList.add(item);
}
result.setItemList(itemList);
//返回结果
return result;
}
}