Solr 6.5.1集群部署和后台管理

  • 两年前用过solr5.1版本的,当时只是简单入个门,拿来在项目里建个全文索引,然后再query,其他什么也没做,还是傻傻地自己去配Tomcat,这次做毕设因为需求而重拾solr,基于作死折腾的原则,肯定要用最新版的啦~,然后。。。还是很顺利踩到坑了,schema相关配置变了。。。配置变了。。。置变了。。。变了。。。了。。。不过还是很快解决了,当然这次打算把集群高可用相关也折腾折腾、还有和mysql之间的配合和索引同步,所以新写一篇看起来不那么low的博客出来(之前写过一篇弱文。。。)

下载和初始化启动

官网下载6.5.1版本的solr,解压

目录说明:
  • bin:启动脚本
  • contrib:第三方贡献拓展jar包,包括一些第三方分词器
  • dist:一些最终生成的jar包,主要是solrj客户端jar包
  • docs:文档
  • example:一些样例,example-DIH目录下的是一些solr索引core样例
  • server:solr服务端工作目录,自带集成jetty插件方式启动solr服务器
    • solr:solr搜索引擎工作目录,即solr/home
    • solr-webapp:solr后台管理页面webapp
    • start.jar:服务端启动jar包
准备工作
  1. contrib/analysis-extras/lucene-libs:这个目录下是第三方分词器jar,将其拷贝到server/solr-webapp/webapp/WEB-INF/lib下供webapp使用
  2. dist/solrj-lib:这里是solrj客户端的依赖jar包,使用maven的话用如下坐标即可
<dependency>
       <groupId>org.apache.solr</groupId>
       <artifactId>solr-solrj</artifactId>
       <version>6.5.1</version>
</dependency>
  1. example/example-DIH/solr:该目录下自带了初始的五个索引库core,将其拷贝到server/solr目录下,solr服务端启动才会自动引入core到系统中(solr.xml不要复制)
  2. server/solr:步骤3拷贝过来的五个core中有conf一系列配置文件可以配置这个core相关参数,后面专门细讲
索引库core配置

到配置好的server/solr目录下,找到db目录为例,db/conf目录下一堆的配置文件,主要配置的为如下:

  • db/lib:存放该core需要的额外jar,比如第三方分词器,在该目录下的分词器中的类可以在schema配置时直接全限定类名引入
  • db/conf/managed-schema:schema配置文件,该文件为系统REST API配置时系统的合成文件,不建议手动修改
  • db/conf/schema.xml:我自己把managed-schema复制一份重命名的,用于手动配置schema
  • db/conf/solrconfig.xml:该core的总配置文件

说明

  • 系统生成和手动编辑可能产生重叠,系统生成的编辑可能会删除注释或者其他帮助理解域、域类型的关键性的自定义内容。你可能希望通过源码控制标记(配置)文件的版本,或者同时限制手动编辑。
    Solrconfig.xml允许Solr schema被定义为“被管理的索引模式”:只能通过Schema API修改schema。
  • managed-schema是solr开始才有的schema配置文件,它是用来避免API方式配置schema和手动修改schema配置文件之间的冲突(比如说重复字段名),因此这个文件不建议手动修改
  • 可以把managed-schema复制一份schema.xml,然后在这个文件手动配置schema
  • 这两个schema配置文件solr系统如何识别呢?答案是两个配置是不能同时生效的,系统默认使用managed-schema,支持REST API方式动态配置schema,适合有动态配置schema、field、fieldType之类需求的就使用这个配置,什么都不改即可。
  • 若项目没有动态修改schema的需求,那可以使用schema.xml进行配置,在其中可以很方便根据schema配置语法进行个性化自定义,方便编码,若要schema.xml生效,需要按照如下步骤修改db/conf/solrconfig.xml的配置:
  1. 找到如下配置(6.5.1其实默认没有这个配置,自己直接添加就行):
<schemaFactory class="ManagedIndexSchemaFactory">
    <bool name="mutable">true</bool>
    <str name="managedSchemaResourceName">managed-schema</str>
</schemaFactory>
  1. 替换成:
<schemaFactory class="ClassicIndexSchemaFactory"/>
Schema手动配置Field和FieldType
<!-- 配置一个新field,设定类型 -->
<field name="text_smart" type="text_smart" indexed="true" stored="true" multiValued="true"/>
<!-- 配置一个新的类型,指定分词器 -->
<fieldType name="text_smart" class="solr.TextField" positionIncrementGap="0">
    <analyzer type="index">
      <tokenizer class="org.apache.lucene.analysis.cn.smart.HMMChineseTokenizerFactory"/>
    </analyzer>
    <analyzer type="query">
       <tokenizer class="org.apache.lucene.analysis.cn.smart.HMMChineseTokenizerFactory"/>
    </analyzer>
</fieldType>
<field name="title" type="text_smart" indexed="true" stored="true" multiValued="true"/>
<field name="text_all" type="text_smart" indexed="true" stored="false" multiValued="true" />
<field name="filename" type="text_smart" indexed="true" stored="true" multiValued="true"/>
<field name="filecontent" type="text_smart" indexed="true" stored="false" multiValued="true"/>
<!-- 为多个field指定一个共同的别名,通过这个别名进行query就能够同时对所有field进行搜索 -->
<copyField source="title" dest="text_all"/>
<copyField source="text_smart" dest="text_all"/>
<copyField source="filename" dest="text_all"/>
<copyField source="filecontent" dest="text_all"/>
<copyField source="id" dest="text_all"/>
启动solr
bin/solr start

启动如下:

solr-start.png

浏览器访问http://localhost:8983/,如下:
solr-admin.png

关闭solr
bin/solr stop

关闭如下:


solr-stop.png

集群部署

Solr集群架构图
solr-cloud-struct.png
  • 图的上半部分是物理结构,一看就清楚,需要关注的是每个机器节点都可以有多个core,这个和下面的逻辑结构有比较大的联系。
  • 下半部分逻辑结构:一个Collection就是一个完整的索引集,可以理解为一个索引文件,Shard1和Shard2就是该索引集的多个分片,就是说把完整的一个索引文件分成了多个,类似数据库分库分表,不过你在访问的时候只需要访问Collection即可,内部分片和索引所在的分片位置不需要关心,这样对不同个分片的访问就可以分流到不同机器上就行处理,也避免了单个索引文件无限膨胀的可能。
  • 再往上就是副本,每个Shard都可以有多个副本,类比数据库读写分离,写操作由Master节点接收,并自动同步到Slave节点,当一个Master节点宕机,就会自动选举一个Slave节点重新承担Master的功能,这里的每个副本就如图所示,对应每个Solr节点的一个Core,这样逻辑和物理之间的联系就建立起来了。
高可用
  • SolrCloud集群由ZooKeeper进行协调,集群中所有节点使用ZooKeeper中共有的配置文件,这样就能保证各个节点之间的配置统一。
  • SolrCloud节点宕机、Master选举等都通过ZooKeeper的分布式协调实现,所以说SolrJ的访问地址也是ZooKeeper的地址。
SolrCloud集群部署
  • 这里比较重要了,由于Solr的版本间兼容性较差,又因为我这是当前最新版,因此在集群部署过程中遇到很多坑,主要是查的资料各种版本都有,还分成tomcat和jetty两种部署方式,搞得我在试验各种方案的过程中浪费了很多时间,好在最后让我找到了一个比较可靠的博客,很详细地用最简单的jetty方式部署,其实从各方面性能看,jetty不比tomcat差,而且现在都流行直接nginx反向代理和负载均衡以及微服务,我反而更喜欢jetty了。。。
  1. ZooKeeper集群部署,这在我之前的博客有,需要的自行查看,我就直接那部署好的ZooKeeper来用而已:ZooKeeper配置和学习笔记
  2. 官网下载solr-6.5.1.tgz,解压并到该目录下
  3. 上传配置文件到ZooKeeper(之后整个集群所有机器就是共用这套配置文件了):
./bin/solr zk -z localhost:2181/solr -upconfig -n solr -d example/example-DIH/solr/solr

参数说明:

-upconfig      表示把你的配置文件上传到ZooKeeper集群
-n configName  指定这个配置的名称,solr管理页面会用到
-d confdir     要上传的配置文件在本地的地址,默认使用解压的example/example-DIH/solr/solr即可,若有配置需求的,可实现修改相应的配置,再上传到ZooKeeper(配置方法同上面的单节点模式)
-z zkHost      Zookeeper集群地址
  1. 把刚才解压的整个solr-6.5.1目录额外复制两份(伪分布式,你若有多台机器可考虑真实分布式部署)
  2. 启动solr节点服务:
./bin/solr start -c -m 1g -z localhost:2181/solr -p 8983

参数说明:

-c:cloud模式启动
-m:最大内存使用
-z:zookeeper集群地址
-p:启动服务端口
  1. 相同方式启动另外两个节点:(端口号错开)
solr-2/bin/solr start -c -m 1g -z localhost:2181/solr -p 8984
solr-2/bin/solr start -c -m 1g -z localhost:2181/solr -p 8985
  1. 浏览器访问:http://localhost:8983/solr/#/
  2. 如图创建Collection:


    solr6.0.1-collections-addCollection.png
  • 配置参数说明:
name: 待创建Collection的名称
config set: collection在zookeeper中的配置目录
numShards: 分片的数量
replicationFactor: 复制副本的数量
maxShardsPerNode:默认值为1,注意三个数值:numShards、replicationFactor、liveSolrNode,一个正常的solrCloud集群不容许同一个liveSolrNode上部署同一个shard的多个replic,因此当maxShardsPerNode=1时,numShards*replicationFactor>liveSolrNode时,报错。因此正确时因满足以下条件:
numShards*replicationFactor<liveSolrNode*maxShardsPerNode
  1. 查看各个节点和分片的关系图:


    solr-6.5.1-collection-graph.png
  • 至此,SolrCloud集群部署完成。
  • 可以看到solr2这个Collection有shard1、shard2、shard3三个分片,每个分片有两个副本,一个Master、一个Slave,黑点为当前处于Master的副本节点。
  • 这时候我们随便打开一个solr节点的目录server/solr,如下:


    solr-6.5.1-node-date.png
  • 可知,刚才管理界面创建的Collection节点的分片以core的形式存在于每个节点的core列表目录下,进入其中一个文件夹查看:


    solr-6.5.1-node-date-dir.png
  • 只有core.properties和data,上面的单节点模式每个core目录下还有lib和conf的,但是这里没有,下面图示说明:


    Solr-Cloud-Core.png

客户端访问操作

单节点访问
/**
 * Solrj客户端工具类,实现对Solr服务的创建索引和查找操作
 *
 * @author linyuqiang 2017/05/10
 */
public class SolrClient {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    /**
     * Solr本地地址
     */
    private String url = "http://localhost:8983/solr/db";
    //查询字符串key
    private String searchKey = "text_all";
    private boolean highlighting = true;
    private String highlightingPre = "<em>";
    private String highlightingPost = "</em>";
    private String highlightingField = "content";
    // Solr客户端对象
    private HttpSolrClient client;

    public SolrClient open() {
        // 1.创建SolrServer对象,以下这两个是线程安全的SolrServer实现类
        // CommonsHttpSolrServer 基于Http协议进行C/S数据交互
        // EmbeddedSolrServer
        // 内嵌式,只要设定好solr的home目录即可实现和solr的交互,不需要开启solr的服务器,本地交互
        client = new HttpSolrClient(url);
        client.setSoTimeout(10000); // socket read timeout
        client.setConnectionTimeout(1000);
        client.setDefaultMaxConnectionsPerHost(100);
        client.setMaxTotalConnections(100);
        client.setFollowRedirects(false); // defaults to false
        // allowCompression defaults to false.
        // Server side must support gzip or deflate for this to have any effect.
        client.setAllowCompression(true);
        return this;
    }

    /**
     * 查找,分页查询
     *
     * @param qString 查找字符串
     * @return 返回查询结果列表
     */
    public List<SolrjMessage> search(String qString, Integer pageNum, Integer pageSiez) {
        try {
            SolrQuery query = new SolrQuery(searchKey + ":" + qString);// 查询字符串
            query.setStart((pageNum - 1) * pageSiez);// 设置查询开始下标
            query.setRows(pageSiez);// 查询行数

            //高亮配置
            query.setHighlight(highlighting);
            query.setHighlightSimplePre(highlightingPre);
            query.setHighlightSimplePost(highlightingPost);
            query.addHighlightField(highlightingField);

            QueryResponse response = client.query(query);// 获取查询返回对象

            SolrDocumentList docs = response.getResults();// 获取查询得到的所有Document
            //查询高亮信息
            Map<String, Map<String, List<String>>> highlightings = response.getHighlighting();
            List<SolrjMessage> list = new ArrayList<SolrjMessage>();
            for (SolrDocument doc : docs) {
                // 获取每个Document的详细信息
                SolrjMessage message = new SolrjMessage();
                message.setId((String) doc.getFieldValue("id"));
                message.setUrl((String) doc.getFieldValue("url"));

                //高亮信息
                List<String> hights = highlightings.get(message.getId() + "").get(highlightingField);
                StringBuilder highlighting = new StringBuilder();
                for (int i = 0; i < hights.size(); i++) {
                    highlighting.append(hights.get(i)).append(" ");
                }
                message.setHighlighting(highlighting.toString());

                list.add(message);
            }

            //其他查询信息
            NamedList responseHeader = response.getResponseHeader();
            SimpleOrderedMap params = (SimpleOrderedMap) responseHeader.get("params");
            NamedList<Object> responseResponse = response.getResponse();
            SolrDocumentList responseBean = (SolrDocumentList) responseResponse.get("response");

            int qTime = response.getQTime();
            int status = response.getStatus();
            Object q = params.get("q");
            Object fq = params.get("fq");
            Object numFound = responseBean.getNumFound();
            Object start = responseBean.getStart();
            return list;
        } catch (SolrServerException e) {
            logger.error("solr服务异常", e);
            throw new RuntimeException("solr服务异常", e);
        } catch (IOException e) {
            logger.error("solr服务IO异常", e);
            throw new RuntimeException("solr服务IO异常", e);
        }
    }

    /**
     * Solr创建索引
     *
     * @param message 创建索引的文件
     */
    public SolrClient createIndex(SolrjMessage message) {
        SolrInputDocument doc = new SolrInputDocument();
        doc.addField("id", message.getId());
        doc.addField("url", message.getUrl());
        doc.addField("content", message.getContent());
        try {
            client.add(doc);
            client.commit();
        } catch (SolrServerException e) {
            logger.error("solr服务异常", e);
            throw new RuntimeException("solr服务异常", e);
        } catch (IOException e) {
            logger.error("solr服务IO异常", e);
            throw new RuntimeException("solr服务IO异常", e);
        }
        return this;
    }

    public void deleteIndex() throws IOException, SolrServerException {
        UpdateResponse response = client.deleteByQuery("docName:testCatcher");
        client.commit();
        int status = response.getStatus();
        System.out.println("status = " + status);
    }
}
集群访问
  • client对象的初始化方式改变,其他创建索引、删除索引、搜索的代码都和单节点一样。
    /**
     * ZooKeeper地址
     */
    private String zookeeperUrl = "{zookeeperHost}:2181/solr";
    //查询字符串key
    private String searchKey = "name";
    private boolean highlighting = true;
    private String highlightingPre = "<em>";
    private String highlightingPost = "</em>";
    private String highlightingField = "content";
    // Solr客户端对象
    private CloudSolrClient client;

    public CloudSolrClient1 open() {
        // 1.创建SolrServer对象,以下这两个是线程安全的SolrServer实现类
        // CommonsHttpSolrServer 基于Http协议进行C/S数据交互
        // EmbeddedSolrServer
        // 内嵌式,只要设定好solr的home目录即可实现和solr的交互,不需要开启solr的服务器,本地交互
        client = new CloudSolrClient(zookeeperUrl);
        client.setSoTimeout(10000); // socket read timeout
        //指定Collection名称
        client.setDefaultCollection("solr");
        client.setZkClientTimeout(30000);
        client.setZkConnectTimeout(30000);
        client.setSoTimeout(30000);
        return this;
    }

管理界面使用说明

  • Collection管理界面,可以添加、删除、查看、别名Collection


    Collection-manager.png
  • 选中其中一个Collection可以进行操作的菜单栏


    solr-6.5.1-overview.png
  • 分词器测试页面,可以对自己配置的分词器手动输入字符串进行分词结果测试


    solr-6.5.1-analysis.png
  • 手动输入document的测试页面,使用不多


    solr-6.5.1-document.png
  • 查看当前Collection的配置文件列表,使用不多


    solr-6.5.1-files.png
  • 搜索查询测试页面,支持分页、排序、高亮、过滤等查询参数的配置,执行按钮点击之后可以在右边页面查看查询的结果信息


    solr-6.5.1-query.png
  • Schema管理页面,可以增加、删除、查看三种类型的Field


    solr-6.5.1-schema.png
  • 选中其中一个Core可以进行操作的菜单栏


    solr-6.5.1-core.png

数据库索引同步

  1. solrconfig.xml配置数据导入加载类:
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
        <lst name="defaults">
            <str name="config">solr-data-config.xml</str>
        </lst>
</requestHandler>
  • 要放在<requestHandler name="/select" class="solr.SearchHandler">之前
  1. 相同目录下创建solr-data-config.xml配置数据库连接信息:
<dataConfig>
  <dataSource name="source1" type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/doc_searcher" user="root" password="" batchSize="-1" />  
  <document>  
        <entity name="test" pk="id"  dataSource="source1"   
                query="select * from  test"  
                 deltaImportQuery="select * from test where id='${dih.delta.id}'"  
                deltaQuery="select id from test where create_time > '${dataimporter.last_index_time}'">  
          <field column="id" name="id"/>  
            <field column="name" name="name"/>  
            <field column="create_time" name="createTime"/>  
     </entity>  
  </document>
</dataConfig>
  • 很简单,照抄小改就行,用jdbc四要素进行连接,delta相关参数是增量导入用的,solr内部维护好上次导入的id和时间戳,这次增量导入就根据内部维护的时间戳进行对比,把变化的部分重新导入到solr。
  • 需要注意的是<field>标签的name属性要在solr的该core的schema进行相应的配置,保证这些field是存在的才能正常运行。
  1. server/solr-webapp/webapp/WEB-INF/lib放入mysql的jdbc驱动包
  2. 清空该Core中的所有数据,或者新建一个全新的Core
  3. 到solr管理页面导入数据即可:


    solr-6.5.1-dataimport.png
其他用到的命令
  • 本地配置同步到ZooKeeper:(一个个文件同步)
./server/scripts/cloud-scripts/zkcli.sh -zkhost localhost:2181 -cmd putfile /solr/configs/solr/solrconfig.xml  example/example-DIH/solr/solr/conf/solrconfig.xml
  • 重新加载solr集群的节点配置:(同步ZooKeeper之后免重启集群,重新加载配置信息)
http://localhost:8983/solr/admin/collections?action=RELOAD&name=solr
  • 从数据源导入数据到Solr
http://localhost:8983/solr/solr2/dataimport?command=full-import&commit=ture

感谢

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容