SolrCloud部署和使用手册
SolrCloud部署和使用手册
文档修订摘要
日期 |
版本 |
描述 |
著者 |
审阅者 |
2013-12-23 |
0.1 |
将txt简易模板的文档提取到word模板。 |
张乐雷 |
|
2013-12-23 |
0.2 |
创建collection的url中制定了createNodeSet |
张乐雷 |
|
2013-12-29 |
0.3 |
1、 solr.war直接使用solr发布的文件,不在进行修改。 2、 日志jar和配置放置到tomcat/lib目录 3、 新增维护document的命令,提供了不同方式来删除或新增索引的方法 4、 新增直接查询solr日志的输出文件 |
张乐雷 |
|
2014-2-22 |
0.4 |
1、 对文档格式大幅改造。 2、 新增监控 3、 增加了zookeeper的维护和监控命令 4、 细化了tomcat/zookeeper操作命令和脚本 5、 新增查看zookeeper日志的方法 6、 增加章节:日常运维 7、 增加章节:系统环境说明,包括:平台环境、软件环境 |
张乐雷 |
|
2014-3-3 |
0.5 |
1、 新增章节:文件管理。 2、 调整的zk 的upconfig命令。 3、 细化solr collection的命令说明。 4、 常见问题,新增scheme.xml变化、新增collection如何操作的步骤。 |
张乐雷 |
|
2014-3-28 |
0.6 |
1、常见问题目录,新增如何删除索引数据 |
张乐雷 |
|
2014-3-31 |
0.7 |
1、丰富日志查看方式 2、增加汇总日志目录 |
张乐雷 |
|
2014-4-6 |
0.8 |
1、 针对solr升级到4.7.1版本,增加常用部署脚本章节 2、 常见问题新增:如何在日志中定位关键字并输出到文件? |
张乐雷 |
|
2014-4-20 |
0.9 |
1、常见问题新增:6.2 生产环境发布需要检查或配置内容有那些? |
张乐雷 |
|
2014-4-28 |
1.0 |
1、常见问题新增:新增sheme.xml如何部署 |
张乐雷 |
|
2014-4-29 |
1.0.1 |
针对新增sheme.xml如何部署问题,进行详细说明,并给出具体实例。 |
张乐雷 |
|
2014-7-23 |
1.1 |
1、针对zk地址变化后,需要配置cs_cfg_solr对应的租户表 2、AICS.CS_CFG_SOLR变更为按租户分表,对其进行了详细描述。 |
张乐雷 |
|
2014-8-15 |
1.2 |
1、为解决无法update问题,新增加章节:软件异常处理 2、问题解答章节,新增:如何原子更新文件指定字段。 |
张乐雷 |
|
2014-10-23 |
1.3 |
1、细化部署方案 |
张乐雷 |
|
2014-11-11 |
1.4 |
1、细化zk日志配置 |
张乐雷 |
|
2014-12-18 |
1.5 |
1、验证环境是否搭建成功,界面是否正常打开、能否正常查询数据。 |
张乐雷 |
|
2015-1-12 |
1.6 |
1、常见问题,删除solr的使用方法 |
张乐雷 |
|
1.7 |
1、新增快速重新搭建环境步骤,针对两个ip的环境的第一次探索部署成功。 |
张乐雷 |
|
|
2015-2-10 |
1.8 |
1、如果配置zookeeper日志输出目录 |
张乐雷 |
|
2015-3-6 |
1.9 |
1、修改zookeeper输出日志配置方式 2、删除环境重置命令章节 |
张乐雷 |
|
2015-3-9 |
2.0 |
1、修改了创建collection和replica的方式 2、修改章节“如果配置zookeeper日志输出目录”调整zk日志输出方式,屏蔽zookeeper.out输出内容。 |
张乐雷 |
|
2015-3-28 |
2.1 |
1、增加优化章节 2、将异常处理迁移到常见问题章节 3、增加zookeeper的详细使用说明 |
张乐雷 |
|
2015-3-29 |
2.2 |
1、丰富应用章节。新增:应用概述、简单例子、规划设计、搜索配置、如何进行索引操作、如何进行搜索、接口封装 |
张乐雷 |
|
目录
3.4.5 配置基于zookeeper的分布式的solr服务... 29
3.4.6 SolrCloud使用Zookeeper管理集群的基本流程... 29
3.5.5 上传zookeeper文件和链接collection. 31
3.5.7 创建collection和replica. 32
3.6.8 上传zookeeper文件和链接collection. 35
3.6.10 创建collection和replica. 36
4.4.1 Solr Schema设计(如何定制索引的结构?)43
4.7.3 函数查询(FunctionQuery)... 65
7.4 如何配置zookeeper日志输出目录?... 97
7.10 新增租户(即创建collection),如何操作?... 101
7.11 在用zkCli.sh启动客户端时,Willnot attempt to authenticate using SASL (无法定位登录配置)103
7.12 Zookeeper的ip或port变更涉及到的调整?... 103
7.18.1 异常java.io.IOException:Packet len4883899 is out of range!109
7.18.2 然后solr数据、zk数据后,仍无法创建collection,报错。... 110
第1章 引言
1.1 文档用途
该文档是对SolrCloud的介绍、部署、开发、维护等方面的说明性文档。
1.2 阅读对象
部署人员、运维人员、开发人员。
1.3 名词术语
- Node : A JVM instance running Solr; a server.
- Cluster : A cluster is a set of Solr nodes managed as a single unit.
- Core : An individual Solr instance (represents a logical index). Multiple cores can run on a single node.
- Shard : In Solr, a logical section of a single collection. This may be spread across multiple nodes of the cluster. Each shard can have as many replicas as needed.
- Leader : Each shard has one node identified as its leader. All the writes for documents belonging to a shard are routed through the leader.
- Collection (Solr) : Multiple documents that make up one logical index. A collection must have a single schema, but can be spread over multiple cores. A cluster can have multiple collections. When you create a collection, you specify its number of shards. You may have many collections in the same set of servers, each with a different number of shards.
- ZooKeeper : Apache ZooKeeper keeps track of configuration and naming, among other things, for a cluster of Solr nodes. A ZooKeeper cluster is used as (1) the central configuration store for the cluster, (2) a coordinator for operations requiring distributed synchronization, and (3) the system of record for cluster topology.
- Ensemble : Multiple ZooKeeper instances running simultaneously.(同时的)
- Collection (ZooKeeper) : A group of cores managed together as part of a SolrCloud installation.
- Overseer : The Overseer coordinates the clusters. It keeps track of the existing nodes and shards and assigns shards to nodes.
- Transaction Log : An append-only log of write operations maintained by each node.
- Document : A group of fields and their values. Documents are the basic unit of storage, and their specific locations are found using an index. Documents are assigned to shards using standard hashing, or by specifically assigning a shard within the document ID. Documents are versioned after each write operation.
1.4 参考资料
1. http://wiki.apache.org/solr/SolrCloud
2. http://www.solrcn.com/books
3. https://cwiki.apache.org/confluence/display/solr
4. http://wiki.apache.org/solr/Solrj
5. http://zookeeper.apache.org/doc/r3.4.3/zookeeperAdmin.html官网zookeeper维护。
第2章 介绍
2.1 简介
Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口。用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引;也可以通过Http Get操作提出查找请求,并得到XML格式的返回结果。
Solr是一个高性能,采用Java开发,基于Lucene的全文搜索服务器。同时对其进行了扩展,提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展并对查询性能进行了优化,并且提供了一个完善的功能管理界面,是一款非常优秀的全文搜索引擎。
文档通过Http利用XML加到一个搜索集合中。查询该集合也是通过http收到一个XML/JSON响应来实现。它的主要特性包括:高效、灵活的缓存功能,垂直搜索功能,高亮显示搜索结果,通过索引复制来提高可用性,提供一套强大Data Schema来定义字段,类型和设置文本分析,提供基于Web的管理界面等。
SOLR逻辑运行图如下:
图一
图二
基本可以用图二这幅图来概述,这是一个拥有4个Solr节点的集群,索引分布在两个Shard里面,每个Shard包含两个Solr节点,一个是Leader节点,一个是Replica节点,此外集群中有一个负责维护集群状态信息的Overseer节点,它是一个总控制器。集群的所有状态信息都放在Zookeeper集群中统一维护。从图中还可以看到,任何一个节点都可以接收索引更新的请求,然后再将这个请求转发到文档所应该属于的那个Shard的Leader节点,Leader节点更新结束完成,最后将版本号和文档转发给同属于一个Shard的replicas节点。
2.2 功能
集中式的配置信息使用ZK进行集中配置。启动时可以指定把Solr的相关配置文件上传Zookeeper,多机器共用。这些ZK中的配置不会再拿到本地缓存,Solr直接读取ZK中的配置信息。配置文件的变动,所有机器都可以感知到。另外,Solr的一些任务也是通过ZK作为媒介发布的。目的是为了容错。接收到任务,但在执行任务时崩溃的机器,在重启后,或者集群选出候选者时,可以再次执行这个未完成的任务。
自动容错SolrCloud对索引分片,并对每个分片创建多个Replication。每个Replication都可以对外提供服务。一个Replication挂掉不会影响索引服务。更强大的是,它还能自动的在其它机器上帮你把失败机器上的索引Replication重建并投入使用。
近实时搜索立即推送式的replication(也支持慢推送)。可以在秒内检索到新加入索引。
查询时自动负载均衡SolrCloud索引的多个Replication可以分布在多台机器上,均衡查询压力。如果查询压力大,可以通过扩展机器,增加Replication来减缓。
自动分发的索引和索引分片发送文档到任何节点,它都会转发到正确节点。
事务日志事务日志确保更新无丢失,即使文档没有索引到磁盘。
其它值得一提的功能有:
索引存储在HDFS上索引的大小通常在G和几十G,上百G的很少,这样的功能或许很难实用。但是,如果你有上亿数据来建索引的话,也是可以考虑一下的。我觉得这个功能最大的好处或许就是和下面这个“通过MR批量创建索引”联合实用。
通过MR批量创建索引有了这个功能,你还担心创建索引慢吗?
强大的RESTful API通常你能想到的管理功能,都可以通过此API方式调用。这样写一些维护和管理脚本就方便多了。
优秀的管理界面主要信息一目了然;可以清晰的以图形化方式看到SolrCloud的部署分布;当然还有不可或缺的Debug功能。
2.3 概念
Collection:在SolrCloud集群中逻辑意义上的完整的索引。它常常被划分为一个或多个Shard,它们使用相同的ConfigSet。如果Shard数超过一个,它就是分布式索引,SolrCloud让你通过Collection名称引用它,而不需要关心分布式检索时需要使用的和Shard相关参数。
Config Set: Solr Core提供服务必须的一组配置文件。每个config set有一个名字。最小需要包括solrconfig.xml(SolrConfigXml)和schema.xml(SchemaXml),除此之外,依据这两个文件的配置内容,可能还需要包含其它文件。它存储在Zookeeper中。Config sets可以重新上传或者使用upconfig命令更新,使用Solr的启动参数bootstrap_confdir指定可以初始化或更新它。
Core: 也就是Solr Core,一个Solr中包含一个或者多个SolrCore,每个Solr Core可以独立提供索引和查询功能,每个Solr Core对应一个索引或者Collection的Shard,Solr Core的提出是为了增加管理灵活性和共用资源。在SolrCloud中有个不同点是它使用的配置是在Zookeeper中的,传统的Solr core的配置文件是在磁盘上的配置目录中。
Leader: 赢得选举的Shard replicas。每个Shard有多个Replicas,这几个Replicas需要选举来确定一个Leader。选举可以发生在任何时间,但是通常他们仅在某个Solr实例发生故障时才会触发。当索引documents时,SolrCloud会传递它们到此Shard对应的leader,leader再分发它们到全部Shard的replicas。
Replica: Shard的一个拷贝。每个Replica存在于Solr的一个Core中。一个命名为“test”的collection以numShards=1创建,并且指定replicationFactor设置为2,这会产生2个replicas,也就是对应会有2个Core,每个在不同的机器或者Solr实例。一个会被命名为test_shard1_replica1,另一个命名为test_shard1_replica2。它们中的一个会被选举为Leader。
Shard: Collection的逻辑分片。每个Shard被化成一个或者多个replicas,通过选举确定哪个是Leader。
Zookeeper: Zookeeper提供分布式锁功能,对SolrCloud是必须的。它处理Leader选举。Solr可以以内嵌的Zookeeper运行,但是建议用独立的,并且最好有3个以上的主机。
2.4 架构
2.4.1索引(collection)的逻辑图
2.4.2Solr和索引对照图
2.4.3创建索引过程
2.4.4分布式查询
2.4.5Shard Splitting
2.4.6近实时搜索和实时获取
NRT 近实时搜索Solr的建索引数据是要在提交时写入磁盘的,这是硬提交,确保即便是停电也不会丢失数据;为了提供更实时的检索能力,Solr设定了一种软提交方式。软提交(soft commit):仅把数据提交到内存,index可见,此时没有写入到磁盘索引文件中。
一个通常的用法是:每1-10分钟自动触发硬提交,每秒钟自动触发软提交。
RealTime Get 实时获取允许通过唯一键查找任何文档的最新版本数据,并且不需要重新打开searcher。这个主要用于把Solr作为NoSQL数据存储服务,而不仅仅是搜索引擎。Realtime Get当前依赖事务日志,默认是开启的。另外,即便是Soft Commit或者commitwithin,get也能得到真实数据。注:commitwithin是一种数据提交特性,不是立刻,而是要求在一定时间内提交数据.
第3章 部署
3.1 系统环境
3.1.1平台环境
平 台 |
运行client |
运行server |
开发环境 |
生产环境 |
GNU/Linux |
√ |
√ |
√ |
√ |
Sun Solaris |
√ |
√ |
√ |
√ |
FreeBSD |
√ |
ⅹ,对nio的支持不好 |
√ |
√ |
Win32 |
√ |
√ |
√ |
ⅹ |
MacOSX |
√ |
√ |
√ |
ⅹ |
注:运行client是指作为客户端,与server进行数据通信,而运行server是指将ZK作为服务器部署运行。
3.1.2软件环境
ZooKeeper Server是一个Java语言实现的分布式协调服务框架,因此需要6或更高版本的JDK支持。集群的机器数量方面,宽泛的讲,其实是任意台机器都可以部署运行的,注意,这里并没有说一定要奇数台机器哦!通常情况下,建议使用3台独立的Linux服务器构成的一个ZK集群。
3.2 部署方案设计
我们常说的ZooKeeper能够提供高可用分布式协调服务,是要基于以下两个条件:
1. 集群中只有少部分的机器不可用。这里说的不可用是指这些机器或者是本身down掉了,或者是因为网络原因,有一部分机器无法和集群中其它绝大部分的机器通信。例如,如果ZK集群是跨机房部署的,那么有可能一些机器所在的机房被隔离了。
2. 正确部署ZK server,有足够的磁盘存储空间以及良好的网络通信环境。
下面将会从集群和单机两个维度来说明,帮助zookeeper管理员尽可能地提高ZK集群的可用性。
3.2.1部署方式
3.2.1.1 集群维度
在上面提到的“过半存活即可用”特性中已经讲到过,整个集群如果对外要可用的话,那么集群中必须要有过半的机器是正常工作并且彼此之间能够正常通信。基于这个特性,那么如果想搭建一个能够允许F台机器down掉的集群,那么就要部署一个由2xF+1 台机器构成的ZK集群。因此,一个由3台机器构成的ZK集群,能够在down掉一台机器后依然正常工作,而5台机器的集群,能够对两台机器down掉的情况容灾。注意,如果是一个6台机器构成的ZK集群,同样只能够down掉两台机器,因为如果down掉3台,剩下的机器就没有过半了。基于这个原因,ZK集群通常设计部署成奇数台机器。
所以,为了尽可能地提高ZK集群的可用性,应该尽量避免一大批机器同时down掉的风险,换句话说,最好能够为每台机器配置互相独立的硬件环境。举个例子,如果大部分的机器都挂在同一个交换机上,那么这个交换机一旦出现问题,将会对整个集群的服务造成严重的影响。其它类似的还有诸如:供电线路,散热系统等。其实在真正的实践过程中,如果条件允许,通常都建议尝试跨机房部署。毕竟多个机房同时发生故障的机率还是挺小的。
3.2.1.2 单机维度
对于ZK来说,如果在运行过程中,需要和其它应用程序来竞争磁盘,CPU,网络或是内存资源的话,那么整体性能将会大打折扣。
首先来看看磁盘对于ZK性能的影响。客户端对ZK的更新操作都是永久的,不可回退的,也就是说,一旦客户端收到一个来自server操作成功的响应,那么这个变更就永久生效了。为做到这点,ZK会将每次更新操作以事务日志的形式写入磁盘,写入成功后才会给予客户端响应。明白这点之后,你就会明白磁盘的吞吐性能对于ZK的影响了,磁盘写入速度制约着ZK每个更新操作的响应。为了尽量减少ZK在读写磁盘上的性能损失,不仿试试下面说的几点:
A、使用单独的磁盘作为事务日志的输出(比如我们这里的ZK集群,使用单独的挂载点用于事务日志的输出)。事务日志的写性能确实对ZK性能,尤其是更新操作的性能影响很大,所以想办法搞到一个单独的磁盘吧!ZK的事务日志输出是一个顺序写文件的过程,本身性能是很高的,所以尽量保证不要和其它随机写的应用程序共享一块磁盘,尽量避免对磁盘的竞争。
B、尽量避免内存与磁盘空间的交换。如果希望ZK能够提供完全实时的服务的话,那么基本是不允许操作系统触发此类swap的。因此在分配JVM堆大小的时候一定要非常小心,具体在本文最后的“注意事项”章节中有讲到。
3.2.2部署图
3.2.3主机和软件
建议将2个刀片机,专门作为搜索服务器,不做其他用途,也就是不混用。
2主机+3zookeeper+ 4tomcat + 每个collection有2shard +每个shard4个replica
1. 2台物理主机。
2. 4个tomcat,即instance(node),每台主机部署2个tomcat。
3. 3个zookeeper。一台主机部署2个zookeeper、两外部署1个。共计3个。
4. 可部署多个collection,每个collection划分为2个shard。
5. 每个shard有4个replica,分别分配到4个tomcat上面
3.2.4存储空间评估
1、 Solr存储数据来源
a) 知识库
i. 文章
b) 工单
i. 咨询和投诉单
ii. Order异常单
iii. SFF异常单
2、 工单存储评估
空间占用的大小与solr索引存储字段多少和大小有关。
实际测试:针对75个字段的存储。
100万个文档所占的容量为4replica* 150M=600M。
1000万为:6G
1亿为:60G
3、 知识库存储评估
a) 以贵州和甘肃为例,大约文档在5-10万个左右,文件索引最大占用:0.5G-1G。
4、 工单存储评估
a) 按照每天1万(工单2000条,任务单8000条),每月30万,每年300万。
i. 5年1500万,4个replica,估计占用空间:30G左右(单个replica,估计占用空间:8G左右)。
ii. 10年3000万,4个replica,估计占用空间:60G左右。(单个replica,估计占用空间:15G左右)
b) 按照每天1万(工单2000条,任务单8000条),每月30万,每年300万。
i. 5年1500万,4个replica,估计占用空间:30G左右(单个replica,估计占用空间:8G左右)。
ii. 10年3000万,4个replica,估计占用空间:60G左右。(单个replica,估计占用空间:15G左右)
c) 按照每天2万(工单4000条,任务单16000条),每月60万,每年600万。
i. 5年3000万。4个replica,估计占用空间:60G左右。(单个replica,估计占用空间:15G左右)
ii. 10年6000万。4个replica,估计占用空间:120G左右。(单个replica,估计占用空间:30G左右)
5、 Linux占用空间
最低要求:10G
6、 日志占用空间
最低要求:20G
7、 其他软件占用空间
最低要求:20G
总结:
记录数1000万 |
记录数5000万 |
记录数1亿(按照该条件搭建环境) |
60G(数据10G+其他50G) |
100G(数据50G+其他50G) |
150G(数据100G+其他50G) |
备注:记录数为1亿的计算方式是:每天2万(工单4000条,任务单16000条),每月60万,每年600万,每5年3000万,每10年6000万。
针对telenor一个租户计算:
1、 每天1000工单+4000任务单=5000条,
2、 每月5000条*30天=15万
3、 每年15万条*12月=180万
4、 5年180万条*5=900万
5、10年180万条*10=1800万
通常上线前,如果用户体验好,需要迁移历史数据,如果迁移前3年的数据,数据量会更一些。
solr主机的最低要求:硬盘至少200G、内存至少32G
3.2.5内存要求
推荐主机内存最小:32G
1、 每个tomcat中部署一个solr实例(solr.war),启动内存设置建议最小:4G
2、 Zookeeper,默认内存启动就行。
从CHD摘录的信息:
The only way to ensure an appropriateamount of memory is to consider your requirements and experiment in your environment.In general:
4 GB may be acceptable for smaller loads orfor evaluation.
12 GB is sufficient for some productionenvironments.
48 GB is sufficient for most situations.
3.3 文件管理
针对solrcloud文件夹列表:
文件夹 |
用途 |
cloud-script |
Solrcloud的脚本文件 |
config-files |
Solrcloud collection对应的配置文件 |
contrib |
Solrcloud中不同collection使用到的公共jar文件 |
dist |
Solrcloud本身发布的jar |
lib |
Solrcloud每个实例instance启动所需的jar文件 |
solrhome开头的文件夹 |
存放solrcloud的solr.xml、索引文件 每个solr 实例instance对应一个solrhome文件夹 |
zookeeper开头的文件夹 |
存放zk的快照文件、日志文件 |
3.4 Zookeeper安装
3.4.1介绍
Zookeeper 分布式服务框架是 Apache Hadoop的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。本文将从使用者角度详细介绍 Zookeeper的安装和配置文件中各个配置项的意义,以及分析 Zookeeper的典型的应用场景(配置文件的管理、集群管理、同步锁、Leader选举、队列管理等),用 Java实现它们并给出示例代码。
3.4.2软件
1. 下载 http://hadoop.apache.org/zookeeper/
3.4.3单机安装
单机安装非常简单,只要获取到 Zookeeper 的压缩包并解压到某个目录如:/home/zookeeper-3.2.2 下,Zookeeper 的启动脚本在 bin 目录下,Linux 下的启动脚本是 zkServer.sh,在 3.2.2 这个版本 Zookeeper 没有提供 windows 下的启动脚本,所以要想在 windows 下启动 Zookeeper 要自己手工写一个,如清单 1 所示:
清单 1. Windows 下 Zookeeper 启动脚本
setlocal set ZOOCFGDIR=%~dp0%..\conf set ZOO_LOG_DIR=%~dp0%.. set ZOO_LOG4J_PROP=INFO,CONSOLE set CLASSPATH=%ZOOCFGDIR%
set CLASSPATH=%~dp0..\*;%~dp0..\lib\*;%CLASSPATH% set CLASSPATH=%~dp0..\build\classes;%~dp0..\build\lib\*;%CLASSPATH% set ZOOCFG=%ZOOCFGDIR%\zoo.cfg set ZOOMAIN=org.apache.zookeeper.server.ZooKeeperServerMain java "-Dzookeeper.log.dir=%ZOO_LOG_DIR%" "-Dzookeeper.root.logger=%ZOO_LOG4J_PROP%" -cp "%CLASSPATH%" %ZOOMAIN% "%ZOOCFG%" %* endlocal |
在你执行启动脚本之前,还有几个基本的配置项需要配置一下,Zookeeper 的配置文件在 conf 目录下,这个目录下有 zoo_sample.cfg 和 log4j.properties,你需要做的就是将 zoo_sample.cfg 改名为 zoo.cfg,因为 Zookeeper 在启动时会找这个文件作为默认配置文件。下面详细介绍一下,这个配置文件中各个配置项的意义。
tickTime=2000 dataDir=D:/devtools/zookeeper-3.2.2/build clientPort=2181 |
- tickTime:这个时间是作为 Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个 tickTime 时间就会发送一个心跳。
- dataDir:顾名思义就是 Zookeeper 保存数据的目录,默认情况下,Zookeeper 将写数据的日志文件也保存在这个目录里。
- clientPort:这个端口就是客户端连接 Zookeeper 服务器的端口,Zookeeper 会监听这个端口,接受客户端的访问请求。
当这些配置项配置好后,你现在就可以启动 Zookeeper 了,启动后要检查 Zookeeper 是否已经在服务,可以通过 netstat – ano 命令查看是否有你配置的 clientPort 端口号在监听服务。
3.4.4集群安装
Zookeeper 不仅可以单机提供服务,同时也支持多机组成集群来提供服务。实际上 Zookeeper 还支持另外一种伪集群的方式,也就是可以在一台物理机上运行多个 Zookeeper 实例,下面将介绍集群模式的安装和配置。
Zookeeper 的集群模式的安装和配置也不是很复杂,所要做的就是增加几个配置项。集群模式除了上面的三个配置项还要增加下面几个配置项:
initLimit=5 syncLimit=2 server.1=192.168.211.1:2888:3888 server.2=192.168.211.2:2888:3888 |
- initLimit:这个配置项是用来配置 Zookeeper 接受客户端(这里所说的客户端不是用户连接 Zookeeper 服务器的客户端,而是 Zookeeper 服务器集群中连接到 Leader 的 Follower 服务器)初始化连接时最长能忍受多少个心跳时间间隔数。当已经超过 10 个心跳的时间(也就是 tickTime)长度后 Zookeeper 服务器还没有收到客户端的返回信息,那么表明这个客户端连接失败。总的时间长度就是 5*2000=10 秒
- syncLimit:这个配置项标识 Leader 与 Follower 之间发送消息,请求和应答时间长度,最长不能超过多少个 tickTime 的时间长度,总的时间长度就是 2*2000=4 秒
- server.A=B:C:D:其中 A 是一个数字,表示这个是第几号服务器;B 是这个服务器的 ip 地址;C 表示的是这个服务器与集群中的 Leader 服务器交换信息的端口;D 表示的是万一集群中的 Leader 服务器挂了,需要一个端口来重新进行选举,选出一个新的 Leader,而这个端口就是用来执行选举时服务器相互通信的端口。如果是伪集群的配置方式,由于 B 都是一样,所以不同的 Zookeeper 实例通信端口号不能一样,所以要给它们分配不同的端口号。
除了修改 zoo.cfg 配置文件,集群模式下还要配置一个文件 myid,这个文件在 dataDir 目录下,这个文件里面就有一个数据就是 A 的值,Zookeeper 启动时会读取这个文件,拿到里面的数据与 zoo.cfg 里面的配置信息比较从而判断到底是那个 server。
3.4.4.1 数据模型
Zookeeper 会维护一个具有层次关系的数据结构,它非常类似于一个标准的文件系统,如图 1 所示:
Zookeeper 这种数据结构有如下这些特点:
1. 每个子目录项如 NameService 都被称作为 znode,这个 znode 是被它所在的路径唯一标识,如Server1 这个znode 的标识为/NameService/Server1
2. znode 可以有子节点目录,并且每个znode 可以存储数据,注意EPHEMERAL 类型的目录节点不能有子节点目录
3. znode 是有版本的,每个 znode 中存储的数据可以有多个版本,也就是一个访问路径中可以存储多份数据
4. znode 可以是临时节点,一旦创建这个znode 的客户端与服务器失去联系,这个znode 也将自动删除,Zookeeper的客户端和服务器通信采用长连接方式,每个客户端和服务器通过心跳来保持连接,这个连接状态称为session,如果znode 是临时节点,这个session 失效,znode也就删除了
5. znode 的目录名可以自动编号,如App1 已经存在,再创建的话,将会自动命名为App2
6. znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个是Zookeeper 的核心特性,Zookeeper的很多功能都是基于这个特性实现的,后面在典型的应用场景中会有实例介绍
3.4.4.2 如何使用
Zookeeper 作为一个分布式的服务框架,主要用来解决分布式集群中应用系统的一致性问题,它能提供基于类似于文件系统的目录节点树方式的数据存储,但是 Zookeeper 并不是用来专门存储数据的,它的作用主要是用来维护和监控你存储的数据的状态变化。通过监控这些数据状态的变化,从而可以达到基于数据的集群管理,后面将会详细介绍 Zookeeper 能够解决的一些典型问题,这里先介绍一下,Zookeeper 的操作接口和简单使用示例。
常用接口列表
客户端要连接 Zookeeper 服务器可以通过创建 org.apache.zookeeper. ZooKeeper 的一个实例对象,然后调用这个类提供的接口来和服务器交互。
前面说了 ZooKeeper 主要是用来维护和监控一个目录节点树中存储的数据的状态,所有我们能够操作 ZooKeeper 的也和操作目录节点树大体一样,如创建一个目录节点,给某个目录节点设置数据,获取某个目录节点的所有子目录节点,给某个目录节点设置权限和监控这个目录节点的状态变化。
表 1 org.apache.zookeeper. ZooKeeper 方法列表
方法名 |
方法功能描述 |
String create(String path, byte[] data, List<ACL> acl,CreateMode createMode) |
创建一个给定的目录节点 path, 并给它设置数据,CreateMode 标识有四种形式的目录节点,分别是 PERSISTENT:持久化目录节点,这个目录节点存储的数据不会丢失;PERSISTENT_SEQUENTIAL:顺序自动编号的目录节点,这种目录节点会根据当前已近存在的节点数自动加 1,然后返回给客户端已经成功创建的目录节点名;EPHEMERAL:临时目录节点,一旦创建这个节点的客户端与服务器端口也就是 session 超时,这种节点会被自动删除;EPHEMERAL_SEQUENTIAL:临时自动编号节点 |
判断某个 path 是否存在,并设置是否监控这个目录节点,这里的 watcher 是在创建 ZooKeeper 实例时指定的 watcher,exists方法还有一个重载方法,可以指定特定的watcher |
|
重载方法,这里给某个目录节点设置特定的 watcher,Watcher 在 ZooKeeper 是一个核心功能,Watcher 可以监控目录节点的数据变化以及子目录的变化,一旦这些状态发生变化,服务器就会通知所有设置在这个目录节点上的 Watcher,从而每个客户端都很快知道它所关注的目录节点的状态发生变化,而做出相应的反应 |
|
删除 path 对应的目录节点,version 为 -1 可以匹配任何版本,也就删除了这个目录节点所有数据 |
|
List<String>getChildren(String path, boolean watch) |
获取指定 path 下的所有子目录节点,同样 getChildren方法也有一个重载方法可以设置特定的 watcher 监控子节点的状态 |
给 path 设置数据,可以指定这个数据的版本号,如果 version 为 -1 怎可以匹配任何版本 |
|
获取这个 path 对应的目录节点存储的数据,数据的版本等信息可以通过 stat 来指定,同时还可以设置是否监控这个目录节点数据的状态 |
|
voidaddAuthInfo(String scheme, byte[] auth) |
客户端将自己的授权信息提交给服务器,服务器将根据这个授权信息验证客户端的访问权限。 |
给某个目录节点重新设置访问权限,需要注意的是 Zookeeper 中的目录节点权限不具有传递性,父目录节点的权限不能传递给子目录节点。目录节点 ACL 由两部分组成:perms 和 id。 |
|
获取某个目录节点的访问权限列表 |
除了以上这些上表中列出的方法之外还有一些重载方法,如都提供了一个回调类的重载方法以及可以设置特定 Watcher 的重载方法,具体的方法可以参考 org.apache.zookeeper.ZooKeeper 类的 API 说明。
下面给出基本的操作 ZooKeeper 的示例代码,这样你就能对 ZooKeeper 有直观的认识了。下面的清单包括了创建与 ZooKeeper 服务器的连接以及最基本的数据操作:
// 创建一个与服务器的连接 ZooKeeper zk = new ZooKeeper("localhost:" + CLIENT_PORT, ClientBase.CONNECTION_TIMEOUT, new Watcher() { // 监控所有被触发的事件 public void process(WatchedEvent event) { System.out.println("已经触发了" + event.getType() + "事件!"); } }); // 创建一个目录节点 zk.create("/testRootPath", "testRootData".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // 创建一个子目录节点 zk.create("/testRootPath/testChildPathOne", "testChildDataOne".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT); System.out.println(new String(zk.getData("/testRootPath",false,null))); // 取出子目录节点列表 System.out.println(zk.getChildren("/testRootPath",true)); // 修改子目录节点数据 zk.setData("/testRootPath/testChildPathOne","modifyChildDataOne".getBytes(),-1); System.out.println("目录节点状态:["+zk.exists("/testRootPath",true)+"]"); // 创建另外一个子目录节点 zk.create("/testRootPath/testChildPathTwo", "testChildDataTwo".getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT); System.out.println(new String(zk.getData("/testRootPath/testChildPathTwo",true,null))); // 删除子目录节点 zk.delete("/testRootPath/testChildPathTwo",-1); zk.delete("/testRootPath/testChildPathOne",-1); // 删除父目录节点 zk.delete("/testRootPath",-1); // 关闭连接 zk.close(); |
输出的结果如下:
已经触发了 None 事件! testRootData [testChildPathOne] 目录节点状态:[5,5,1281804532336,1281804532336,0,1,0,0,12,1,6] 已经触发了 NodeChildrenChanged 事件! testChildDataTwo 已经触发了 NodeDeleted 事件! 已经触发了 NodeDeleted 事件! |
当对目录节点监控状态打开时,一旦目录节点的状态发生变化,Watcher 对象的 process 方法就会被调用。
3.4.4.3 典型的应用场景
Zookeeper 从设计模式角度来看,是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,Zookeeper 就将负责通知已经在 Zookeeper 上注册的那些观察者做出相应的反应,从而实现集群中类似 Master/Slave 管理模式,关于Zookeeper 的详细架构等内部细节可以阅读 Zookeeper 的源码
下面详细介绍这些典型的应用场景,也就是 Zookeeper 到底能帮我们解决那些问题?下面将给出答案。
分布式应用中,通常需要有一套完整的命名规则,既能够产生唯一的名称又便于人识别和记住,通常情况下用树形的名称结构是一个理想的选择,树形的名称结构是一个有层次的目录结构,既对人友好又不会重复。说到这里你可能想到了 JNDI,没错Zookeeper 的 Name Service与 JNDI 能够完成的功能是差不多的,它们都是将有层次的目录结构关联到一定资源上,但是 Zookeeper 的 Name Service 更加是广泛意义上的关联,也许你并不需要将名称关联到特定资源上,你可能只需要一个不会重复名称,就像数据库中产生一个唯一的数字主键一样。
Name Service 已经是 Zookeeper 内置的功能,你只要调用 Zookeeper 的 API 就能实现。如调用 create 接口就可以很容易创建一个目录节点。
配置的管理在分布式应用环境中很常见,例如同一个应用系统需要多台 PC Server 运行,但是它们运行的应用系统的某些配置项是相同的,如果要修改这些相同的配置项,那么就必须同时修改每台运行这个应用系统的 PC Server,这样非常麻烦而且容易出错。
像这样的配置信息完全可以交给 Zookeeper 来管理,将配置信息保存在 Zookeeper 的某个目录节点中,然后将所有需要修改的应用机器监控配置信息的状态,一旦配置信息发生变化,每台应用机器就会收到 Zookeeper 的通知,然后从Zookeeper 获取新的配置信息应用到系统中。
Zookeeper 能够很容易的实现集群管理的功能,如有多台 Server 组成一个服务集群,那么必须要一个“总管”知道当前集群中每台机器的服务状态,一旦有机器不能提供服务,集群中其它集群必须知道,从而做出调整重新分配服务策略。同样当增加集群的服务能力时,就会增加一台或多台 Server,同样也必须让“总管”知道。
Zookeeper 不仅能够帮你维护当前的集群中机器的服务状态,而且能够帮你选出一个“总管”,让这个总管来管理集群,这就是 Zookeeper 的另一个功能Leader Election。
它们的实现方式都是在 Zookeeper 上创建一个EPHEMERAL 类型的目录节点,然后每个 Server 在它们创建目录节点的父目录节点上调用 getChildren(String path,boolean watch) 方法并设置 watch 为 true,由于是EPHEMERAL 目录节点,当创建它的 Server 死去,这个目录节点也随之被删除,所以 Children 将会变化,这时 getChildren上的 Watch 将会被调用,所以其它 Server 就知道已经有某台 Server 死去了。新增 Server 也是同样的原理。
Zookeeper 如何实现 Leader Election,也就是选出一个Master Server。和前面的一样每台 Server 创建一个 EPHEMERAL 目录节点,不同的是它还是一个 SEQUENTIAL 目录节点,所以它是个EPHEMERAL_SEQUENTIAL 目录节点。之所以它是EPHEMERAL_SEQUENTIAL 目录节点,是因为我们可以给每台 Server 编号,我们可以选择当前是最小编号的 Server 为 Master,假如这个最小编号的 Server 死去,由于是 EPHEMERAL 节点,死去的 Server 对应的节点也被删除,所以当前的节点列表中又出现一个最小编号的节点,我们就选择这个节点为当前 Master。这样就实现了动态选择 Master,避免了传统意义上单 Master 容易出现单点故障的问题。
这部分的示例代码如下,完整的代码请看附件:
void findLeader() throws InterruptedException { byte[] leader = null; try { leader = zk.getData(root + "/leader", true, null); } catch (Exception e) { logger.error(e); } if (leader != null) { following(); } else { String newLeader = null; try { byte[] localhost = InetAddress.getLocalHost().getAddress(); newLeader = zk.create(root + "/leader", localhost, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); } catch (Exception e) { logger.error(e); } if (newLeader != null) { leading(); } else { mutex.wait(); } } } |
共享锁在同一个进程中很容易实现,但是在跨进程或者在不同 Server 之间就不好实现了。Zookeeper 却很容易实现这个功能,实现方式也是需要获得锁的 Server 创建一个EPHEMERAL_SEQUENTIAL 目录节点,然后调用 getChildren方法获取当前的目录节点列表中最小的目录节点是不是就是自己创建的目录节点,如果正是自己创建的,那么它就获得了这个锁,如果不是那么它就调用exists(String path,boolean watch) 方法并监控 Zookeeper 上目录节点列表的变化,一直到自己创建的节点是列表中最小编号的目录节点,从而获得锁,释放锁很简单,只要删除前面它自己所创建的目录节点就行了。
同步锁的实现代码如下,完整的代码请看附件:
void getLock() throws KeeperException, InterruptedException{ List<String> list = zk.getChildren(root, false); String[] nodes = list.toArray(new String[list.size()]); Arrays.sort(nodes); if(myZnode.equals(root+"/"+nodes[0])){ doAction(); } else{ waitForLock(nodes[0]); } } void waitForLock(String lower) throws InterruptedException, KeeperException { Stat stat = zk.exists(root + "/" + lower,true); if(stat != null){ mutex.wait(); } else{ getLock(); } } |
Zookeeper 可以处理两种类型的队列:
1. 当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达,这种是同步队列。
2. 队列按照 FIFO 方式进行入队和出队操作,例如实现生产者和消费者模型。
同步队列用 Zookeeper 实现的实现思路如下:
创建一个父目录 /synchronizing,每个成员都监控标志(Set Watch)位目录 /synchronizing/start 是否存在,然后每个成员都加入这个队列,加入队列的方式就是创建/synchronizing/member_i 的临时目录节点,然后每个成员获取 /synchronizing 目录的所有目录节点,也就是 member_i。判断 i 的值是否已经是成员的个数,如果小于成员个数等待 /synchronizing/start 的出现,如果已经相等就创建 /synchronizing/start。
用下面的流程图更容易理解:
同步队列的关键代码如下,完整的代码请看附件:
void addQueue() throws KeeperException, InterruptedException{ zk.exists(root + "/start",true); zk.create(root + "/" + name, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); synchronized (mutex) { List<String> list = zk.getChildren(root, false); if (list.size() < size) { mutex.wait(); } else { zk.create(root + "/start", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } } |
当队列没满是进入 wait(),然后会一直等待Watch 的通知,Watch 的代码如下:
public void process(WatchedEvent event) { if(event.getPath().equals(root + "/start") && event.getType() == Event.EventType.NodeCreated){ System.out.println("得到通知"); super.process(event); doAction(); } } |
FIFO 队列用 Zookeeper 实现思路如下:
实现的思路也非常简单,就是在特定的目录下创建 SEQUENTIAL 类型的子目录 /queue_i,这样就能保证所有成员加入队列时都是有编号的,出队列时通过 getChildren( ) 方法可以返回当前所有的队列中的元素,然后消费其中最小的一个,这样就能保证 FIFO。
下面是生产者和消费者这种队列形式的示例代码,完整的代码请看附件:
boolean produce(int i) throws KeeperException, InterruptedException{ ByteBuffer b = ByteBuffer.allocate(4); byte[] value; b.putInt(i); value = b.array(); zk.create(root + "/element", value, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); return true; } |
int consume() throws KeeperException, InterruptedException{ int retvalue = -1; Stat stat = null; while (true) { synchronized (mutex) { List<String> list = zk.getChildren(root, true); if (list.size() == 0) { mutex.wait(); } else { Integer min = new Integer(list.get(0).substring(7)); for(String s : list){ Integer tempValue = new Integer(s.substring(7)); if(tempValue < min) min = tempValue; } byte[] b = zk.getData(root + "/element" + min,false, stat); zk.delete(root + "/element" + min, 0); ByteBuffer buffer = ByteBuffer.wrap(b); retvalue = buffer.getInt(); return retvalue; } } } } |
Zookeeper 作为 Hadoop 项目中的一个子项目,是 Hadoop 集群管理的一个必不可少的模块,它主要用来控制集群中的数据,如它管理 Hadoop 集群中的 NameNode,还有Hbase 中 MasterElection、Server 之间状态同步等。
本文介绍的Zookeeper 的基本知识,以及介绍了几个典型的应用场景。这些都是Zookeeper 的基本功能,最重要的是Zoopkeeper 提供了一套很好的分布式集群管理的机制,就是它这种基于层次型的目录树的数据结构,并对树中的节点进行有效管理,从而可以设计出多种多样的分布式的数据管理模型,而不仅仅局限于上面提到的几个常用应用场景。
3.4.5配置基于zookeeper的分布式的solr服务
分三种情况
1) 一台zookeeper服务器,一台solr服务器。
2) 一台zookeeper服务器,多台solr服务器。
3) 多台zookeeper服务器,多台solr服务器。
3.4.6SolrCloud使用Zookeeper管理集群的基本流程
第一台Solr服务器启动过程:
1、 启动第一台zookeeper服务器,作为集群状态信息的管理者
2、 将自己这个节点注册到/node_states/目录下,同时将自己注册到/live_nodes/目录下
3、 创建/overseer_elect/leader,为后续Overseer节点的选举做准备,新建一个Overseer
4、 更新/clusterstate.json目录下json格式的集群状态信息
5、 本机从Zookeeper中更新集群状态信息,维持与Zookeeper上的集群信息一致
6、 上传本地配置文件到Zookeeper中,供集群中其他solr节点使用,后面启动solr则不
会上传配置文件,因为都是使用第一台solr服务启动上传的配置文件为准.
7、 启动本地的Solr服务器,Overseer会得知shard中有第一个节点进来,更新shard状
态信息,并将本机所在节点设置为shard1的leader节点,并向整个集群发布最新
的集群状态信息。
8、 本机从Zookeeper中再次更新集群状态信息,第一台solr服务器启动完毕。
第二台solr服务器的启动过程:
1. 连接到集群所在的Zookeeper
2. 将自己这个节点注册到/node_states/目录下,同时将自己注册到/live_nodes/目录下
3. 本机从Zookeeper中更新集群状态信息,维持与Zookeeper上的集群信息一致
4. 从集群中保存的配置文件加载Solr所需要的配置信息
5. 启动本地solr服务器,将本节点注册为集群中的shard,并将本机设置为shard2的Leader节点
6. 本机从Zookeeper中再次更新集群状态信息,第二台solr服务器启动完毕.
3.5 简单搭建步骤
3.5.1准备文件
在用户根目录下方式放置下面文件:
shutdowntomcat.sh
shutdownzookeeper.sh
zookeeperstatus.sh
startzookeeper.sh
restartzookeeper.sh
starttomcat.sh
solrcloud.tar.gz
tomcat_solr.tar.gz(里面的webapps中提前放置solr发布dist目录下的war文件,该文件会带有版本号,需手工调整为solr.war)
zookeeper.tar.gz
3.5.2修改配置
1. 修改tomcat server.xml端口
2. 修改catalina.sh中的JAVA_OPTS
3. 修改zoo.cfg配置
特别注意:所有的配置路径都将/home/solrtest,替换为你的新名称/home/{changeme}
建议直接使用现在的端口,将solrtest应用全部停掉。释放端口资源。甚至可以删除solrtest用户。
3.5.3日志配置
6、 复制solr-*.*.*\example\lib\ext下面的jar到tomcat\lib目录。
7、 复制solr-*.*.*\example\resources\log4j.properties到tomcat\lib目录。
8、 将所有日志统一输出到logs目录
3.5.4启动zookeeper
startzookeeper.sh
通过zookeeperstatus.sh可查看zookeeper的启动状态。
3.5.5上传zookeeper文件和链接collection
1. 目前已经有工单、知识库的配置,且已经上传。
2. 但是增加新租户需要在solrcloud/config-files下面单独建立一个目录,将上一个租户的配置copy进入,并进行上传。
3. 下次新增租户,每次发布需要你们上传配置文件。
4. collectionName是开发提供的约定好的,在aics.cs_cfg_solr对应的租户表中配置。
3.5.6启动tomcat
一旦启动后,如果后面增加新collection,只要重复第3步。而无需重新启动应用软件。
检查tomcat是否在活动状态:ps -ef|grep solr.solr.home
3.5.7创建collection和replica
-- 需替换下面ip、collectionname
########tm_sff_ticket_21########updatezookeeper config########
~/solrcloud/cloud-scripts/zkcli.sh -cmdupconfig -zkhost 10.226.5.165:48600,10.226.5.165:48605,10.226.5.165:48610-confdir ~/solrcloud/config-files/tm_sff_ticket -confname tm_sff_ticket_21_conf
~/solrcloud/cloud-scripts/zkcli.sh -cmdlinkconfig -collection tm_sff_ticket_21 -confname tm_sff_ticket_21_conf -zkhost10.226.5.165:48600,10.226.5.165:48605,10.226.5.165:48610
########tm_sff_ticket_21########createcollection########
curl"http://10.226.5.165:48500/solr/admin/collections?action=CREATE&name=tm_sff_ticket_21&numShards=2&maxShardsPerNode=4&createNodeSet=10.226.5.165:48500_solr"
curl"http://10.226.5.165:48502/solr/admin/cores?action=CREATE&name=tm_sff_ticket_21_shard1_replica2&collection=tm_sff_ticket_21&shard=shard1"
curl "http://10.226.5.165:48504/solr/admin/cores?action=CREATE&name=tm_sff_ticket_21_shard1_replica3&collection=tm_sff_ticket_21&shard=shard1"
curl"http://10.226.5.165:48506/solr/admin/cores?action=CREATE&name=tm_sff_ticket_21_shard1_replica4&collection=tm_sff_ticket_21&shard=shard1"
curl"http://10.226.5.165:48502/solr/admin/cores?action=CREATE&name=tm_sff_ticket_21_shard2_replica2&collection=tm_sff_ticket_21&shard=shard2"
curl"http://10.226.5.165:48504/solr/admin/cores?action=CREATE&name=tm_sff_ticket_21_shard2_replica3&collection=tm_sff_ticket_21&shard=shard2"
curl"http://10.226.5.165:48506/solr/admin/cores?action=CREATE&name=tm_sff_ticket_21_shard2_replica4&collection=tm_sff_ticket_21&shard=shard2"
3.5.8关闭tomcat
shutdowntomcat.sh
3.5.9关闭zookeeper
shutdownzookeeper.sh
3.6 详细搭建步骤
3.6.1准备solrcloud文件包
该文件包包括:索引的数据和日志、zookeeper的数据和日志、zookeeper配置文件、zookeeper脚本和依赖的jar。
目前已经构建完毕,直接解压后直接使用,无需修改对应的内容。
3.6.2准备solr.war
复制solr/dist/solr-*.*.*.war到webapps目录并修改名称为solr.war,举例:cp solr.tar.gz/home/solrtest/tomcat_solr2_6802/webapps
3.6.3准备管理脚本
这些脚本都是已经已经是准备好的,直接使用即可。
shutdowntomcat.sh
shutdownzookeeper.sh
zookeeperstatus.sh
starttomcat.sh
startzookeeper.sh
restartzookeeper.sh
3.6.4修改tomcat配置
1. <Serverport="6726" shutdown="SHUTDOWN">其中port按照4个node分别分配为:6726/6727/6728/6729
<Connector port="8080".....>其中port按照4个node分别分配为:6801/6802/6803/6803
2. 修改tomcat/bin/catalina.sh文件
-Dsolr.solr.home=/home/solrtest/solrhome1 -DzookeeperHost=localhost:2181,localhost:2182,localhost:2183-Djetty.port=6801
-Dsolr.solr.home=/home/solrtest/solrhome2 -DzookeeperHost=localhost:2181,localhost:2182,localhost:2183-Djetty.port=6802
-Dsolr.solr.home=/home/solrtest/solrhome3 -DzookeeperHost=localhost:2181,localhost:2182,localhost:2183-Djetty.port=6803
-Dsolr.solr.home=/home/solrtest/solrhome4 -DzookeeperHost=localhost:2181,localhost:2182,localhost:2183-Djetty.port=6804
3.6.5修改zookeeper配置
zookeeper必须部署为:2n+1(n是节点数量,即启动的zookeeper应用的数量,绝对不能是偶数)
修改其中的zookeeper*/conf/zoo.cfg文件中,都增加或修改下面的内容。将数据和日志都输出到solrcloud文件夹。
dataDir=/home/solrtest/solrcloud/zookeeper1/data
dataLogDir=/home/solrtest/solrcloud/zookeeper1/log
dataDir=/home/solrtest/solrcloud/zookeeper2/data
dataLogDir=/home/solrtest/solrcloud/zookeeper2/log
dataDir=/home/solrtest/solrcloud/zookeeper3/data
dataLogDir=/home/solrtest/solrcloud/zookeeper3/log
3.6.6日志配置
1、 复制solr-*.*.*\example\lib\ext下面的jar到tomcat\lib目录。
2、 复制solr-*.*.*\example\resources\log4j.properties到tomcat\lib目录。
3、 将所有日志统一输出到logs目录
3.6.7启动zookeeper
startzookeeper.sh
通过zookeeperstatus.sh可查看zookeeper的启动状态。
3.6.8上传zookeeper文件和链接collection
1. 目前已经有工单、知识库的配置,且已经上传。
2. 但是增加新租户需要在solrcloud/config-files下面单独建立一个目录,将上一个租户的配置copy进入,并进行上传。
3. 下次新增租户,每次发布需要你们上传配置文件。
4. collectionName是开发提供的约定好的,在aics.cs_cfg_solr对应的租户表中配置。
实际操作命令:
1. 上传文件
/home/solrtest/solrcloud/cloud-scripts/zkcli.sh -cmd upconfig-zkhost localhost:2181,localhost:2182,localhost:2183 -confdir/home/solrtest/solrcloud/config-files/sr_ticket -confname sr_ticket_01_conf
可在zk/bin下面zkCli.sh -server localhost:2181,查询ls /configs/,看看配置文件是否上传成功。
2. 配置文件与collection关联
/home/solrtest/solrcloud/cloud-scripts/zkcli.sh -cmd linkconfig-collection sr_ticket_01 -confname sr_ticket_01_conf -zkhostlocalhost:2181,localhost:2182,localhost:2183
3.6.9启动tomcat
1. 务必是在上传完毕配置文件和链接完毕文件后,再启动tomcat
2. 一旦启动后,如果后面增加新collection,只要重复第3步。而无需重新启动应用软件。
3. 检查tomcat是否在活动状态:ps-ef|grep solr.solr.home
4. 特别注意有时启动tomcat会时间较长,我们可以通过查看tomcat目录下面的log/的日志,带有日期的日志文件,tail -100f catalina.yyyy-mm-dd.log
启动命令:starttomcat.sh
3.6.10 创建collection和replica
-- 需替换下面ip、collectionname
########tm_sff_ticket_21########updatezookeeper config########
~/solrcloud/cloud-scripts/zkcli.sh -cmdupconfig -zkhost 10.226.5.165:48600,10.226.5.165:48605,10.226.5.165:48610-confdir ~/solrcloud/config-files/tm_sff_ticket -confname tm_sff_ticket_21_conf
~/solrcloud/cloud-scripts/zkcli.sh -cmdlinkconfig -collection tm_sff_ticket_21 -confname tm_sff_ticket_21_conf -zkhost10.226.5.165:48600,10.226.5.165:48605,10.226.5.165:48610
########tm_sff_ticket_21########createcollection########
curl"http://10.226.5.165:48500/solr/admin/collections?action=CREATE&name=tm_sff_ticket_21&numShards=2&maxShardsPerNode=4&createNodeSet=10.226.5.165:48500_solr"
curl"http://10.226.5.165:48502/solr/admin/cores?action=CREATE&name=tm_sff_ticket_21_shard1_replica2&collection=tm_sff_ticket_21&shard=shard1"
curl"http://10.226.5.165:48504/solr/admin/cores?action=CREATE&name=tm_sff_ticket_21_shard1_replica3&collection=tm_sff_ticket_21&shard=shard1"
curl"http://10.226.5.165:48506/solr/admin/cores?action=CREATE&name=tm_sff_ticket_21_shard1_replica4&collection=tm_sff_ticket_21&shard=shard1"
curl"http://10.226.5.165:48502/solr/admin/cores?action=CREATE&name=tm_sff_ticket_21_shard2_replica2&collection=tm_sff_ticket_21&shard=shard2"
curl"http://10.226.5.165:48504/solr/admin/cores?action=CREATE&name=tm_sff_ticket_21_shard2_replica3&collection=tm_sff_ticket_21&shard=shard2"
curl"http://10.226.5.165:48506/solr/admin/cores?action=CREATE&name=tm_sff_ticket_21_shard2_replica4&collection=tm_sff_ticket_21&shard=shard2"
3.6.11 关闭tomcat
shutdowntomcat.sh
3.6.12 关闭zookeeper
shutdownzookeeper.sh
第4章 应用
4.1 应用概述
4.1.1应用模式
1. 对于原有系统已有的数据或需要索引的数据量较大的情况
直接采用通过http方式调用solr的接口方式,效率较差,采用solr本身对csv 的支持(http://wiki.apache.org/solr/UpdateCSV),将数据导出为csv格式,然后调用solr的csv接口http://localhost:8080/solr/update/csv
2. 对于系统新增的数据
先将需要索引查询的数据组装成xml格式,然后使用httpclient 将数据提交到solr的http接口,例如: http://localhost:8080/solr/update
也可以参考post.jar中的SimplePostTool的实现。http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/util/SimplePostTool.java?view=co
3. 中文分词
采用mmseg4j作为solr(Lucene)缺省的中文分词方案
项目库:http://code.google.com/p/mmseg4j/
也可以采用庖丁解牛作为solr(Lucene)缺省的中文分词方案
项目库:http://code.google.com/p/paoding/
4. 与nutch的集成使用
http://blog.foofactory.fi/2007/02/online-indexing-integrating-nutch-with.html
5. 嵌入式Solr
http://wiki.apache.org/solr/Solrj#EmbeddedSolrServer
6. 分布式索引
http://wiki.apache.org/solr/CollectionDistribution
4.1.2使用过程说明
1. 第一步:搜索引擎规划设计
ü 定制好业务模型
ü 定制好索引结构
ü 定制好搜索策略
ü 配置solr的schemal配置文件
2. 第二步:搜索引擎配置
根据搜索引擎的规划,配置solr的schema.xml等配置文件。
3. 第三步:构建索引并定时更新索引
通过调用索引接口进行索引的构建与更新。
4. 第三步:搜索
通过调用搜索接口进行搜索。
4.2 简单例子
1.1.1Solr Schema设计
1.1.2构建索引
找到下载的软件包,在\apache-solr-1.4.1\example\exampledocs目录下创建 mmseg4j-solr-demo-doc.xml 文档,内容如下:
<add> <doc> <field name="id">1</field> <field name="text">昨日,记者从解放军总参谋部相关部门获悉,截至3月28日,解放军和武警部队累计出动7.2万人次官兵支援地方抗旱救灾。组织民兵预备役人员20.2万人 次支援地方抗旱救灾。</field> </doc> <doc> <field name="id">2</field> <field name="text">下半年房价调整就是挤水分 房价回不到去年水平。</field> </doc> <doc> <field name="id">3</field> <field name="text">solr是基于Lucene Java搜索库的企业级全文搜索引擎,目前是apache的一个项目。</field> </doc> <doc> <field name="id">4</field> <field name="text">中国人民银行是中华人民共和国的中央银行。</field> </doc> </add> |
然后在 cmd 切换到“apache-solr-1.4.0\example\exampledocs>”,运行post.jar,命令如下:
上面文件无法直接使用,有特殊字符!
java -Durl=http://192.168.10.85:18080/solr/update -Dcommit=yes -jar post.jar mmseg4j-solr-demo-doc.xml |
注:多核心时为这个地址http://192.168.10.85:18080/solr/core0/update/
1.1.3搜索测试
查看是否有数据
访问:http://192.168.10.85:18080/solr/admin/
在Query String:中输入“中国”,显示如下图所示:
http://192.168.10.85:18080/solr/core0/select/?q=中国&version=2.2&start=0&rows=10&indent=on
4.3 规划设计
搜索应用架构
4.3.1定义业务模型
定义搜索的业务模型即对搜索的需求进行分析,确定搜索的业务对象和结构:
1)确定要搜索的业务对象:
分析确定有哪些内容需要进行搜索,这些内容的来源,更新的频次等信息,按如下结构进行定义:
示例:
编码 |
业务对象 |
优先级 |
内容来源 |
更新频次 |
说明 |
T001 |
天气信息 |
1 |
168平台 |
1小时 |
输入城市名则结果为该城市的天气,没有输入地名则结果为您所属地市的天气信息 |
T002 |
餐饮酒店 |
1 |
互联网 |
每日 |
搜索词可以是地名、特色菜、餐馆名称,搜索结果是与您的搜索词相关的餐馆信息 |
T003 |
航班信息 |
1 |
互联网 |
每日 |
搜索词可以是起始和到港的城市名,搜索结果是与您的搜索词相关的航班信息 |
T004 |
火车时刻 |
1 |
互联网 |
每日 |
搜索词可以是列车车次、起始站点、途经站点,搜索结果是与您的搜索词相关的火车车次详细信息 |
T005 |
电影影讯 |
1 |
12580 |
每日 |
搜索词可以是最新放映的影片名或影院名称,搜索结果是与您的搜索词相关的各影院近期放映的电影影讯。 |
T006 |
股票行情 |
1 |
168平台 |
1分钟 |
搜索词可以是股票代码、股票名称,搜索结果是与您的搜索词相关的即时的股票行情信息。 |
T007 |
移动业务 |
1 |
手工导入 |
每月 |
搜索词可以移动是业务名称,搜索结果是与您的搜索词相关的业务简介和开通方法信息。 |
T008 |
通讯录 |
1 |
手工导入 |
每月 |
|
T009 |
短信 |
1 |
手工导入 |
每日 |
|
T010 |
彩信 |
1 |
手工导入 |
每日 |
|
T011 |
WAP网址 |
1 |
手工导入 |
每日 |
|
2)梳理业务对象的结构
针对以上业务对象进行分析梳理出业务对象的结构。以下是通讯录和wap网址的结构定义
a)通讯录
字段 |
说明 |
ID |
内容标识 |
userName |
姓名 |
userMail |
用户邮箱 |
mobile |
用户手机号 |
department |
部门 |
sex |
性别 |
birthday |
生日 |
msn |
MSN |
|
QQ号 |
b)wap网站
字段 |
说明 |
ID |
内容标识 |
标题 |
内容标题 |
URL地址 |
Wap网站的URL地址 |
标签 |
搜索的依据,多个关键字以","分隔。 |
摘要 |
Wap网站的简要说明 |
网站分类 |
搜索、娱乐、SNS、新闻 |
4.3.2定制索引服务
定制索引服务是对索引源进行结构化的索引,换句话说,索引后的结果是结构化的、有意义的信息。定制索引主要涉及如下几个方面:
1) 分类:多核解决方案
2) 需要检索的字段
3) 需要存储的字段
4) 过滤条件
5) 排序
6) 索引的更新频次
4.4 搜索配置
4.4.1Solr Schema设计(如何定制索引的结构?)
索引的结构配置主要是对schema.xml中的Fieldtype、Fields、copyField、dynamicField的配置,schema.xml的配置文件说明请参考“2.4配置文件说明”的“1 schema.xml”,下面我们以搜索通讯录为例进行说明。
public String userName = null;// 姓名 public String userMail = null;// 用户邮箱 public String mobile = null;// 用户手机号 public String department = null;// public String sex = null;// 性别 public String birthday = null;// 生日 public String msn = null; public String qq = null; |
4.4.1.1 定义好需要的类型(Fieldtype)
定义好需要的类型(Fieldtype),同时为类型(Fieldtype)配置合适的分词器,下面以mmsg4j中文分词为示例,:
<types> …… <!--mmseg4j field types--> <fieldType name="textComplex" class="solr.TextField" positionIncrementGap="100" > <analyzer> <tokenizer class="com.chenlb.mmseg4j.solr.MMSegTokenizerFactory" mode="complex" dicPath="/opt/solr-tomcat/solr/dic"/> <filter class="solr.LowerCaseFilterFactory"/> </analyzer> </fieldType>
<fieldType name="textMaxWord" class="solr.TextField" positionIncrementGap="100" > <analyzer> <tokenizer class="com.chenlb.mmseg4j.solr.MMSegTokenizerFactory" mode="max-word" dicPath="/opt/solr-tomcat/solr/dic"/> <filter class="solr.LowerCaseFilterFactory"/> </analyzer> </fieldType>
<fieldType name="textSimple" class="solr.TextField" positionIncrementGap="100" > <analyzer> <tokenizer class="com.chenlb.mmseg4j.solr.MMSegTokenizerFactory" mode="simple" dicPath="/opt/solr-tomcat/solr/dic"/> <filter class="solr.LowerCaseFilterFactory"/> </analyzer> </fieldType> …… </types> |
注:dicPath="/opt/solr-tomcat/solr/dic "是你的词库路径。
4.4.1.2 定义好需要的字段(Field)
<fields> …… <field name=" username " type="string" indexed="true" stored="true"/> <field name=" usermail " type="text" indexed="true" stored="true"/> <field name=" mobile " type="text" indexed="true" stored="true"/> <field name=" department" type="text" indexed="true" stored="true"/> <field name=" sex" type="text" indexed="true" stored="true"/> <field name=" birthday" type="text" indexed="true" stored="true"/> <field name=" msn" type="text" indexed="true" stored="true"/> <field name=" qq" type="text" indexed="true" stored="true"/> …… </fields> |
<copyField source="simple" dest="text"/> <copyField source="complex" dest="text"/> |
示例: 博客应用程序的声明字段
<field name="keywords" type="text_ws" indexed="true" stored="true" multiValued="true" omitNorms="true"/> <field name="creationDate" type="date" indexed="true" stored="true"/> <field name="rating" type="sint" indexed="true" stored="true"/> <field name="published" type="boolean" indexed="true" stored="true"/> <field name="content" type="text" indexed="true" stored="true" /> <!-- catchall field, containing many of the other searchable text fields (implemented via copyField further on in this schema) --> <field name="all" type="text" indexed="true" stored="true" multiValued="true"/> |
4.5 如何进行索引操作?
4.5.1操作方式
Solr是一个拥有象WebService一样接口的独立运行的搜索服务器。它基于restful风格,并且扩展了Lucene.能够通过HTTP协议以XML格式将文档放入搜索服务器(索引),你能够通过HTTP协议的GET来查询搜索服务器并且得到XML格式的结果。
REST并不是一个协议或技术;它是一种体系结构风格。
REST是 SOAP 的轻量型替代品,它是面向资源的,而不是面向操作的。它常常被归结为远程过程使用 HTTP 调用 GET、POST、PUT 和 DELETE 语句。
REST比较简单朴素,在安全性,可靠消息传输,或标准化的业务过程自动化上还没有定义。
4.5.1.1 通过http传递xml格式数据
[html]view plaincopy
<add>
<doc>
<fieldname="employeeId">05991</field>
<fieldname="office">Bridgewater</field>
<fieldname="skills">Perl</field>
<fieldname="skills">Java</field>
</doc>
[<doc> ... </doc>[<doc> ...</doc>]]
</add>
<delete><id>05991</id></delete>
<delete><query>office:Bridgewater</query></delete>
http://localhost:8983/solr/update?commit=true-H "Content-Type: text/xml" --data-binary '<add><doc><fieldname="id">testdoc</field></doc></add>'
http://localhost:8081/solr/update/?stream.body=<add><doc><fieldname="id">1</field><fieldname="author">bbb</field></doc></add>&commit=true
4.5.1.2 通过http传递json格式数据
[html]view plaincopy
{
add:{
"doc": {
"id": "DOC1",
"my_boosted_field": { /* use a map with boost/value for aboosted field */
"boost": 2.3,
"value": "test"
},
"my_multivalued_field": ["aaa", "bbb" ] /* use an array for a multi-valued field*/
}
},
add:{
"commitWithin": 5000, /* commit this document within 5seconds */
"overwrite": false, /* don't check for existingdocuments with the same uniqueKey */
"boost": 3.45, /* a document boost */
"doc": {
"f1": "v1",
"f1": "v2"
}
},
delete:{ "id":"ID" }, /* delete by ID*/
delete:{ "query":"QUERY" } /* delete by query */
delete:{ "query":"QUERY", 'commitWithin':'500' } /* delete by query, commit within 500ms*/
}
原子操作:
http://localhost:8983/solr/update-H 'Content-type:application/json' -d '
{
"id" : "TestDoc1",
"title" : {"set":"test1"},
"revision" : {"inc":3},
"publisher" :{"add":"TestPublisher"}
}'
http://localhost:8081/solr/update/?stream.body={"add":{"doc":{"id":1,"author":"ccc"}}}&commit=true
查询
http://localhost:8983/solr/select?q=name:monsters&wt=json&indent=true
[html]view plaincopy
{
"responseHeader":{
"status":0,
"QTime":2,
"params":{
"indent":"true",
"wt":"json",
"q":"title:monsters"}},
"response":{"numFound":1,"start":0,"docs":[
{
"id":"978-1423103349",
"author":"RickRiordan",
"series_t":"PercyJackson and the Olympians",
"sequence_i":2,
"genre_s":"fantasy",
"inStock":true,
"price":6.49,
"pages_i":304,
"name":[
"The Sea ofMonsters"],
"cat":["book","paperback"]}]
}}
4.5.1.3 通过java API(Solrj)访问
1、建立与Solr服务的连接
[html]view plaincopy
Stringurl = "http://localhost:8983/solr";
CommonsHttpSolrServerserver = new CommonsHttpSolrServer(url);
server.setSoTimeout(3000);// socket read timeout
server.setConnectionTimeout(1000);
server.setDefaultMaxConnectionsPerHost(1000);
server.setMaxTotalConnections(10);
server.setFollowRedirects(false);// defaults to false
server.setAllowCompression(true);
server.setMaxRetries(1);
2、搜索条件的设置
[html]view plaincopy
SolrQueryquery = new SolrQuery();
query.setQuery("tags:t5AND t7");
query.setStart(0);
query.setRows(4);
query.addSortField("auction_id",SolrQuery.ORDER.desc);
query.addSortField("auction_point",SolrQuery.ORDER.asc);
3、结果集的获取
Solrj提供的注解的方式,注入pojo
[html]view plaincopy
importorg.apache.solr.client.solrj.beans.Field;
public class Item {
@Field
String id;
@Field("tags")
String[] tag;
@Field
List<String> features;
}
调用
[html]view plaincopy
查询
QueryResponse qrsp =solrServer.query(query);
List<Item> productIds =qrsp.getBeans(Item.class);
更新索引
solrServer.addBean(obj);
4.5.2基本索引操作
在 Solr 中,通过向部署在 servlet 容器中的 Solr Web 应用程序发送 HTTP 请求来启动索引。您可以向 Solr 索引 servlet 传递四个不同的索引请求:
1) add/update允许您向 Solr添加文档或更新文档。直到提交后才能搜索到这些添加和更新。
2) commit告诉 Solr,应该使上次提交以来所做的所有更改都可以搜索到。
3) optimize重构 Lucene的文件以改进搜索性能。索引完成后执行一下优化通常比较好。如果更新比较频繁,则应该在使用率较低的时候安排优化。一个索引无需优化也可以正常地运行。优化是一个耗时较多的过程。
4) delete可以通过 id或查询来指定。按 id删除将删除具有指定 id的文档;按查询删除将删除查询返回的所有文档。
4.5.2.1 新增、更新索引
|
<add>
<doc>
<fieldname="url">http://localhost/myBlog/solr-rocks.html</field>
<field name="title">Solr Search is Simply Great</field>
<field name="keywords">solr,lucene,enterprise,search</field>
<fieldname="creationDate">2007-01-06T05:04:00.000Z</field>
<field name="rating">10</field>
<field name="content">Solr is a really great open source searchserver. It scales,
it's easy to configure and the Solr community is reallysupportive.</field>
<field name="published">on</field>
</doc>
</add>
<doc>
中的每个 field条目告诉 Solr 应该将哪些Field
添加到所创建文档的 Lucene索引中。可以向add
命令添加多个<doc>
。通过
HTTP POST
将命令发往:http://localhost:8080/solr/update。如果一切进展顺利,则会随<resultstatus="0"/>
返回一个 XML文档,添加文档成功,如果是相同的 URL 自动更新文档(示例应用程序中的 URL 是 Solr 识别文档以前被添加过所使用的惟一 id)。
注:schema.xml里有一个uniqueKey,的配置,这里将url字段作为索引文档的唯一标识符,非常重要。
<uniqueKey>url</uniqueKey>
4.5.2.2 删除索引
1. 删除制定ID的索引
<delete><id>05138022</id></delete>
2. 删除查询到的索引数据
<delete><query>id:IW-02</query></delete>
3. 删除所有索引数据
<delete><query>*:*</query></delete>
通过
HTTP POST
将命令发往:http://localhost:8080/solr/update
注:以上xml文件通过HTTP POST
将命令发往在http://192.168.10.85:18080/solr/update/,多核心时为这个地址http://192.168.10.85:18080/solr/core0/update/
4.5.3批量索引操作
对原有系统已有的数据或需要索引的数据量较大的情况,需要进行批量的索引操作
4.5.3.1 通过CVS文件的方式提交
直接采用通过http方式调用solr的接口方式,效率较差,采用solr本身对csv的支持(http://wiki.apache.org/solr/UpdateCSV ),将数据导出为csv格式,然后调用solr的csv接口http://localhost:8080/solr/update/csv
具体操作步聚:
1.修改conf/solrconfig.xml
1)新增csv处理配置项
<!-- CSV update handler, loaded on demand -->
<requestHandler name="/update/csv" class="solr.CSVRequestHandler" startup="lazy">
</requestHandler>
2)修改enableRemoteStreaming=true
<requestParsers enableRemoteStreaming="true" multipartUploadLimitInKB="2048" />
2. 下面的命令能直接读取输入文件提交到Solr
curl http://localhost:8983/solr/update/csv?stream.file=exampledocs/books.csv&stream.contentType=text/plain;charset=utf-8
#NOTE: The full path, or a path relative to the CWD of the running solr server must be used.
4.5.3.2 数据库数据导入生成索引(DataImportHandlerDIH)
大多数的应用程序将数据存储在关系数据库、xml文件中。对这样的数据进行搜索是很常见的应用。所谓的DataImportHandler提供一种可配置 的方式向solr导入数据,可以一次全部导入,也可以增量导入。
l 能够读取关系数据库中的数据。
l 通过可配置的方式,能够将数据库中多列、多表的数据生成solr文档
l 能够通过solr文档更新solr
l 提供通过配置文件就能够导入所有数据的能力
l 能够发现并处理 由insert、update带来的变化(我们假定在表中有一个叫做“last-modified的列”)
l 能够配置 “完全导入”和“增量导入”的时间
l 让读取xml文件,并建立索引成为可配置。
l 能够将 其他的数据源(例如:ftp,scp,etc)或者其他格式的文档(Json,csv)以插件的形式集成到项目中。
我们一共需要在两个配置文件中进行一些配置。 定义一个data-config.xml 文件,并把它的路径配置到solrconfig.xml 中,关于DataImportHandler的配置必须在这个文件中配置,datasource也可以。不过,一般将datasource放在data-config.xml文件中。如下所示:
solrconfig.xml文件
<?xml version="1.0"encoding="UTF-8" ?>
<config>
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
<lst name="defaults">
<str name="config">/home/username/data-config.xml</str>
</lst>
</requestHandler>
</config>
data-config.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<dataConfig>
<dataSourcetype="JdbcDataSource"
driver="oracle.jdbc.driver.OracleDriver"
url="jdbc:oracle:thin:@218.17.227.194:1521:kgdb"
user="ehr_test"
password="szkingdomRHIN"/>
<document>
<entity name="EHR_ID" query="SELECT EHR_ID,MPI_ID,EHR_DATE,EHR_TIME,ORG_ID,
DATA_ELEM_SET,ACT_CODE,ACT_SNO,ACT_SNO_CLASS,EHR_DIGEST,CDA_ID,CREATE_DATE,
CANCEL,SNO_ID_INHOS_VERSION FROM T_EHR">
<fieldcolumn="EHR_ID" name="id"/>
<fieldcolumn="MPI_ID" name="MPI_ID"/>
<field column="EHR_DATE"name="EHR_DATE"/>
<field column="EHR_TIME"name="EHR_TIME"/>
<field column="ORG_ID"name="ORG_ID"/>
<field column="DATA_ELEM_SET"name="DATA_ELEM_SET"/>
<field column="ACT_CODE"name="ACT_CODE"/>
<field column="ACT_SNO"name="ACT_SNO"/>
<field column="ACT_SNO_CLASS"name="ACT_SNO_CLASS"/>
<field column="EHR_DIGEST"name="EHR_DIGEST"/>
<field column="CDA_ID"name="CDA_ID"/>
<field column="CREATE_DATE"name="CREATE_DATE"/>
<field column="CANCEL"name="CANCEL"/>
<fieldcolumn="SNO_ID_INHOS_VERSION"name="SNO_ID_INHOS_VERSION"/>
</entity>
</document>
</dataConfig>
打开DataImportHandler页面去验证,是否该配置的都配置好了。http://localhost:8080/solr/dataimport ,使用“完全导入”选项将数据从数据库中导出,并提交给solr建立索引,使用“增量导入”选项对数据库发生的变化的数据导出,并提交给solr建立索引。
一个配置文件可以配置多个数据源。增加一个dataSource元素就可以增加一个数据源了。name属性可以区分不同的数据源。如果配 置了多于一个的数据源,那么要注意将name配置成唯一的。例如:
<?xml version="1.0"encoding="UTF-8"?>
<dataConfig>
<dataSource type="JdbcDataSource" name="ds-1"
driver="oracle.jdbc.driver.OracleDriver"
url="jdbc:oracle:thin:@218.17.227.194:1521:kgdb"
user="ehr_test"
password="szkingdomRHIN"/>
<dataSourcetype="JdbcDataSource" name="ds-2"
driver="oracle.jdbc.driver.OracleDriver"
url="jdbc:oracle:thin:@218.17.227.195:1521:htdb"
user="ehr_test"
password="szkingdomRHIN"/>
<document>
<entity name="EHR_ID" dataSource="ds-1"query="SELECT EHR_ID,MPI_ID,EHR_DATE,
EHR_TIME,ORG_ID FROM T_EHR">
<fieldcolumn="EHR_ID" name="id"/>
<fieldcolumn="MPI_ID" name="MPI_ID"/>
<fieldcolumn="EHR_DATE" name="EHR_DATE"/>
<fieldcolumn="EHR_TIME" name="EHR_TIME"/>
<fieldcolumn="ORG_ID" name="ORG_ID"/>
</entity>
<entity name="EHR_ID" dataSource="ds-2" ......>
......
</entity>
</document>
</dataConfig>
data-config.xml的根元素是document。document是schema,它的域上的值可能来自于多个表.一个document元素代表了一种文档。 一个document元素中包含了一个或者多个root实体。一个root实体包含着一些子实体,这些子实体能够包含其他的实体。实体就是,关系数据库上 的表或者视图。每个实体都能够包含多个域,每个域对应着数据库返回结果中的一列。域的名字跟列的名字默认是一样的。如果一个列的名字跟solr field的名字不一样,那么属性name就应该要给出。其他的需要的属性在solrschema.xml文件中配置。为了能够从数据库中取得想要的数据,我们的设计支持标准sql规范。这使得用户能够使用他任何想要的sql语句。root实体是一个中心表,使用它的列可 以把表连接在一起。
dataconfig的结构
dataconfig 的结构不是一成不变的,entity和field元素中的属性是随意的,这主要取决于processor和transformer。以下是entity的默认属性
Ø name(必需的):name是唯一的,用以标识entity
Ø processor:只有当datasource不是RDBMS时才是必需的。默认值是 SqlEntityProcessor
Ø transformer:转换器将会被应用到这个entity上,详情请浏览transformer部分。
Ø pk:entity的主键,它是可选的,但使用“增量导入”的时候是必需。它跟schema.xml中定义的 uniqueKey没有必然的联系,但它们可以相同。
Ø rootEntity:默认情况下,document元素下就是根实体了,如果没有根实体的话,直接在实体下 面的实体将会被看做跟实体。对于根实体对应的数据库中返回的数据的每一行,solr都将生成一个document。
下面是SqlEntityProcessor的属性
Ø query (required) :sql语句
Ø deltaQuery : 只在“增量导入”中使用
Ø parentDeltaQuery : 只在“增量导入”中使用
Ø deletedPkQuery : 只在“增量导入”中使用
Ø deltaImportQuery : (只在“增量导入”中使用) . 如果这个存在,那么它将会在“增量导入”中导入phase时代替query产生作用。这里有一个命名空间的用法${dataimporter.delta.}
solr导入数据时的参数
Ø clean : (default 'true'). 决定在建立索引之前,删除以前的索引。
Ø commit : (default 'true'). 决定这个操作之后是否要commit
Ø optimize : (default 'true'). 决定这个操作之后是否要优化。
Ø debug : (default false). 工作在debug模式下。
4.6 如何进行搜索(一)
4.6.1搜索语法
4.6.1.1 solr 查询参数说明
1. 常用
1) q - 查询字符串,必须的。
2) fl - 指定返回那些字段内容,用逗号或空格分隔多个。
3) start - 返回第一条记录在完整找到结果中的偏移位置,0开始,一般分页用。
4) rows - 指定返回结果最多有多少条记录,配合start来实现分页。
5) sort - 排序,格式:sort=<fieldname>+<desc|asc>[,<field name>+<desc|asc>]… 。示例:(inStock desc, price asc)表示先 “inStock” 降序, 再 “price” 升序,默认是相关性降序。
注:排序字段 只能针对数值型如:int,dobuble等…
6) wt - (writertype)指定输出格式,可以有 xml, json, php,phps, 后面 solr 1.3增加的,要用通知我们,因为默认没有打开。
7) fq - (filter query)过虑查询,作用:在q查询符合结果中同时是fq查询符合的,例如:q=mm&fq=date_time:[20081001TO 20091031],找关键字mm,并且date_time是20081001到20091031之间的。官方文档:http://wiki.apache.org/solr/CommonQueryParameters#head-6522ef80f22d0e50d2f12ec487758577506d6002
2. 不常用
1) q.op - 覆盖schema.xml的defaultOperator(有空格时用"AND"还是用"OR"操作逻辑),一般默认指定
2) df - 默认的查询字段,一般默认指定
3) qt - (query type)指定那个类型来处理查询请求,一般不用指定,默认是standard。
3. 其它
1) indent - 返回的结果是否缩进,默认关闭,用 indent=true|on 开启,一般调试json,php,phps,ruby输出才有必要用这个参数。
2) version - 查询语法的版本,建议不使用它,由服务器指定默认值。
4.6.1.2 Solr的检索运算符
1. “:” 指定字段查指定值,如返回所有值*:*
2. “?” 表示单个任意字符的通配
3. “*” 表示多个任意字符的通配(不能在检索的项开始使用*或者?符号)
4. “~” 表示模糊检索,如检索拼写类似于”roam”的项这样写:roam~将找到形如foam和roams的单词;roam~0.8,检索返回相似度在0.8以上的记录。
5. 邻近检索,如检索相隔10个单词的”apache”和”jakarta”,”jakarta apache”~10
6. “^” 控制相关度检索,如检索jakarta apache,同时希望去让”jakarta”的相关度更加好,那么在其后加上”^”符号和增量值,即jakarta^4 apache
7. 布尔操作符AND、||
8. 布尔操作符OR、&&
9. 布尔操作符NOT、!、- (排除操作符不能单独与项使用构成查询)
10.“+” 存在操作符,要求符号”+”后的项必须在文档相应的域中存在
11. ( ) 用于构成子查询
12. [] 包含范围检索,如检索某时间段记录,包含头尾,date:[200707 TO 200710]
13. {} 不包含范围检索,如检索某时间段记录,不包含头尾
date:{200707 TO 200710}
注:范围检索字段只适用于:String,int,dobule,date不能用于long型的字段
14. \ 转义操作符,特殊字符包括+ - && || ! ( ) { } [ ] ^ ” ~ * ? : \
4.6.1.3 solr查询的一些常用语法
1、首先假设我的数据里fields有:name, tel,address 预设的搜寻是name这个字段, 如果要搜寻的数据刚好就是 name 这个字段,就不需要指定搜寻字段名称。
2、查询规则:
如欲查询特定字段(非预设字段),请在查询词前加上该字段名称加 “:” (不包含”号) 符号,
例如: address:北京市海淀区上地软件园 tel:88xxxxx1
1>. q代表query input
2>. version代表solr版本(建议不要变动此变量)
3>. start代表显示结果从哪一笔结果资料开始,预设为0代表第一笔, rows是说要显示几笔数据,预设为10笔
(因为有时查询结果可能有几百笔,但不需要显示所有结果,所以预设是从第一笔开始到第十笔)
所以若要显示第10到30笔就改为:
http: //localhost:8080/solr/select/?indent=on&version=2.2&q=address:北京 市海淀区上地软件园+tel:88xxxxx1&version=2.2&start=10&rows=20&indent=on
(indent代表输出的xml要不要缩行.预设为开启 on)
3、另外,要限定输出结果的内容可用 “fl=” 加上你要的字段名称,如以下这个范例:
http://localhost:8080/solr/select/?indent=on&version=2.2&q=text:北京+ OR+text:亿度&start=0&rows=10&fl=name,address,tel
在fl=之后加上了name,adress,tel
所以结果会如下:
<result name=”response” numFound=”1340″ start=”0″>
<doc>
<str name=”name”>北京亿度</str>
<str name=”address”>北京市海淀区上地软件园</str>
<str name=”tel”>88xxxxxx1</str>
</doc>
<doc>
<str name=”name”>北京亿度</str>
<str name=”address”/>
<str name=”tel”>88xxxxxx1</str>
</doc>
</result>
4、查询 name 或 address:直接输入查询词, 如: 亿度
送出的内容即为:
name:亿度 AND address:海淀
5、若要搜寻联集结果,请在词与词间空格或加上大写 “OR” (不包含”号).
例如: text:海淀 OR text:亿度
text:海淀 OR 亿度
或
海淀 亿度
或
name:亿度 OR tel:88xxxxxx1
或
name:亿度 tel:88xxxxxx1
6、若要搜寻交集结果,请在词与词间加上大写 “AND” 或“+” (不包含”号).
例如: text:海淀 AND 亿度
或
+text:海淀 +text:亿度
或
name:亿度 AND tel:88xxxxxx1
或
name: ( +亿度 +海淀)
7、排除查询
在要排除的词前加上“-” (不包含”号) 号
例如: 海淀 -亿度
搜寻结果不会有包含亿度的词的结果在内
8、Group 搜寻
使用 “()” 来包含一个group
如希望搜寻在店名字段内同时有“台北”(不包含”号) 及 “火车站”(不包含”号)
9、增加权重: 如要搜寻 “北京 加油站”(不包含”号) 但因为回传太多笔资料内有 “中华”(不包含”号) 或 “加油站”(不包含”号) 的结果,
所以想要把有包含“加油站”(不包含”号)的数据往前排,可使用 “^”(不包含”号)符号在后面加上愈增加的权重数,
像是 “2″,则可以这样做:
北京 加油站^2
会同时搜寻含有北京或加油站的结果,并把加油站这个词加权所以搜寻时会先判断加油站这一个词在搜寻结果中的比重,甚至假设一笔数据内加油站出现过两次以上的就更加会有优先权。
查询时在查询词后加上“^” (不包含”号) 再加上权重分数
例如: 亿度 AND “北京”^2
或
亿度^2 OR 北京
10、Wildcard 搜寻使用 “*” 符号; 如果输入 “中国*银” (不包含”号), 结果会有中国信托商业银行, 中国输出入银行图书阅览室, 中国商银证券
中国及银之间可夹任何长短字词.
4.6.2字段增加权重
在很多时候,我们可能会需要增加某一个字段的权重,以合理的显示搜索结果。
例如:有一个schma,有三个字段:chapterId, title, content.
我们希望某一个关键字如果在title中匹配了,就要优先显示,而在content中匹配了,就放在搜索结果的后面。当然,如果两者同时匹配当然没什么好说的了。看看solr中如何做到吧。
title:(test1test2)^4 content:(test1 test2)
给title字段增加权重,优先匹配
关于^后面的数字4,经过我测试,最佳值应该是有n个字段就写成n+1,当然希望大家能更好的去测试!
4.6.3Solr分析器
关于lucene的分析器,分词器,过滤器,请看:http://lianj-lee.javaeye.com/blog/501247
对一个document进行索引时,其中的每个field中的数据都会经历分析(根据上面的一个博客可以知道,分析就是组合分词和过滤),最终将一句话分成单个的单词,去掉句子当中的空白符号,大写转换小写,复数转单数,去掉多余的词,进行同义词代换等等。
如:This is ablog! this, is, a 会被去除,最后最剩下blog。当然!这个符号也会被去除的。
这个过程是在索引和查询过程中都会进行的,而且通常两者进行的处理的都是一样的,这样做是为了保证建立的索引和查询的正确匹配。
分析器(Analyzer)
分析器是包括两个部分:分词器和过滤器。分词器功能将句子分成单个的词元token,过滤器就是对词元进行过滤。
solr自带了一些分词器,如果你需要使用自定义的分词器,那么就需要修改schema.xml文件。
schema.xml 文件允许两种方式修改文本被分析的方式,通常只有field类型为 solr.TextField 的field的内容允许定制分析器。
方法一:使用任何org.apache.lucene.analysis.Analyzer的子类进行设定。
<fieldType name="text"class="solr.TextField">
<analyzerclass="org.wltea.analyzer.lucene.IKAnalyzer"/>
</fieldType>
方法二:指定一个TokenizerFactory,后面跟一系列的TokenFilterFactories(它们将按照所列的顺序发生作用),Factories被用来创建分词器和分词过滤器,它们用于对分词器和分词过滤器的准备配置,这样做的目的是为了避免the overhead of creation via reflection。
<analyzer type="index">
<tokenizerclass="org.wltea.analyzer.solr.IKTokenizerFactory"isMaxWordLength="false"/>
……
</analyzer>
<analyzertype="query">
<tokenizerclass="org.wltea.analyzer.solr.IKTokenizerFactory"isMaxWordLength="true"/>
……
</analyzer>
需要说明的一点是,AnyAnalyzer, TokenizerFactory, or TokenFilterFactory 应该用带包名的全类名进行指定,请确保它们位于Solr的classpath 路径下。对于 org.apache.solr.analysis.* 包下的类,仅仅通过solr.*就可以进行指定。
如果你需要使用自己的分词器和过滤器,你就需要自己写一个 factory ,它必须是 BaseTokenizerFactory(分词器) 或BaseTokenFilterFactory(过滤器)的子类。就像下面一样。
public class MyFilterFactory extends BaseTokenFilterFactory {
public TokenStreamcreate(TokenStream input) {
return newMyFilter(input);
}
}
对于IK3.1.5版本已经完全支持了solr的分词,这样就不用自己来编写了, 而对于中文的切词的话,ik对solr的支持已经很完美了。
Solr提供了哪些TokenizerFactories?
1. solr.LetterTokenizerFactory
创建org.apache.lucene.analysis.LetterTokenizer.
分词举例:
"I can't"==> "I", "can", "t",字母切词。
2. solr.WhitespaceTokenizerFactory
创建org.apache.lucene.analysis.WhitespaceTokenizer,主要是切除所有空白字符。
3. solr.LowerCaseTokenizerFactory
创建org.apache.lucene.analysis.LowerCaseTokenizer
分词举例:
"I can't"==> "i", "can", "t",主要是大写转小写。
4. solr.StandardTokenizerFactory
创建org.apache.lucene.analysis.standard.StandardTokenizer
分词举例: "I.B.M. cat's can't" ==>
ACRONYM: "I.B.M.", APOSTROPHE:"cat's",APOSTROPHE:"can't"
说明:该分词器,会自动地给每个分词添加type,以便接下来的对type敏感的过滤器进行处理,目前仅仅只有StandardFilter对Token 的类型是敏感的。
5. solr.HTMLStripWhitespaceTokenizerFactory
从结果中除去HTML标签,将结果交给WhitespaceTokenizer处理。
例子:
my <a href="www.foo.bar">link</a>
my link
<?xml?><br>hello<!--comment-->
hello
hello<script><--f('<--internal--></script>'); --></script>
hello
if a<b then print a;
if a<b then print a;
hello <td height=22 nowrap align="left">
hello
a<b A Alpha&Omega Ω
a<b A Alpha&Omega Ω
6. solr.HTMLStripStandardTokenizerFactory
从结果中出去HTML标签,将结果交给StandardTokenizer处理。
7. solr.PatternTokenizerFactory
说明:按照规则表达式样式对分本进行分词。
例子:处理对象为,mice; kittens; dogs,他们由分号加上一个或多个的空格分隔。
<fieldType name="semicolonDelimited"class="solr.TextField">
<analyzer>
<tokenizerclass="solr.PatternTokenizerFactory" pattern="; *" />
</analyzer>
</fieldType>
Solr有哪些TokenFilterFactories?
1. solr.StandardFilterFactory
创建:org.apache.lucene.analysis.standard.StandardFilter.
移除首字母简写中的点和Token后面的’s。仅仅作用于有类的Token,他们是由StandardTokenizer产生的。
例:StandardTokenizer+ StandardFilter
"I.B.M. cat's can't" ==> "IBM","cat", "can't"
2. solr.LowerCaseFilterFactory
创建:org.apache.lucene.analysis.LowerCaseFilter.
3. solr.TrimFilterFactory【solr1.2】
创建:org.apache.solr.analysis.TrimFilter
去掉Token两端的空白符
例:
" Kittens! ","Duck" ==> "Kittens!", "Duck".
4. solr.StopFilterFactory
创建:org.apache.lucene.analysis.StopFilter
去掉如下的通用词,多为虚词。
"a","an", "and", "are", "as","at", "be", "but", "by",
"for","if", "in", "into", "is","it",
"no","not", "of", "on", "or", "s","such",
"t","that", "the", "their", "then","there", "these",
"they","this", "to", "was", "will","with"
自定义的通用词表的使用可以通过schema.xml文件中的"words"属性来指定,如下。
<fieldtype name="teststop"class="solr.TextField">
<analyzer>
<tokenizerclass="solr.LowerCaseTokenizerFactory"/>
<filterclass="solr.StopFilterFactory" words="stopwords.txt"ignoreCase="true"/>
</analyzer>
</fieldtype>
5. solr.KeepWordFilterFactory【solr1.3】
创建:org.apache.solr.analysis.KeepWordFilter
作用与solr.StopFilterFactory相反,保留词的列表也可以通过”word”属性进行指定。
<fieldtype name="testkeep"class="solr.TextField">
<analyzer>
<filterclass="solr.KeepWordFilterFactory" words="keepwords.txt"ignoreCase="true"/>
</analyzer>
</fieldtype>
6. solr.LengthFilterFactory
创建:solr.LengthFilter
过滤掉长度在某个范围之外的词。范围设定方式见下面。
<fieldtype name="lengthfilt"class="solr.TextField">
<analyzer>
<tokenizerclass="solr.WhitespaceTokenizerFactory"/>
<filterclass="solr.LengthFilterFactory" min="2" max="5"/>
</analyzer>
</fieldtype>
7. solr.PorterStemFilterFactory
创建:org.apache.lucene.analysis.PorterStemFilter
采用Porter Stemming Algorithm算法去掉单词的后缀,例如将复数形式变成单数形式,第三人称动词变成第一人称,现在分词变成一般现在时的动词。
8. solr.EnglishPorterFilterFactory
创建:solr.EnglishPorterFilter
关于句子主干的处理,其中的"protected"指定不允许修改的词的文件。
9. solr.SnowballPorterFilterFactory
关于不同语言的词干处理
10.solr.WordDelimiterFilterFactory
关于分隔符的处理。
11.solr.SynonymFilterFactory
关于同义词的处理。
12.solr.RemoveDuplicatesTokenFilterFactory
避免重复处理。
4.6.4Solr高亮使用
1、SolrQuery类,此类有方法setHighlight(true),当设置为true时,表示开启了高亮。
2、SolrQuery类,有方法:
// 以下给两个字段开启了高亮,分别是name,description,
query.addHighlightField("name");
query.addHighlightField("description");
// 以下两个方法主要是在高亮的关键字前后加上html代码
query.setHighlightSimplePre("<fontcolor=\"red\">");
query.setHighlightSimplePost("</font>");
3、下面是获取高亮的内容:
Map<String,Map<String,List<String>>>map = response.getHighlighting();
Map的Key为document的Id,即你在schema.xml中设置的Id,Value为该Id对应的document的值,Value也为一个Map,该Map的Key为fieldName,Value为List<String>,这个List里面的内容就是该文档的高亮字段。
所以当做逻辑处理的时候,只要按照这个层次,依次把东西给取出来即可,如果取出来的东西为空,则用QueryResponse中的 SolrDocument的getFieldValue(filedName)的值。
对了,请注意在solrConfig.xml中开启高亮组件,这个可以看看官方wiki或者看solrconfig.xml中注释!
4.7 如何进行搜索(二)
4.7.1Query参数
1. CoreQueryParam查询的参数
1) q:查询字符串,必须的。
2) q.op:覆盖schema.xml的defaultOperator(有空格时用"AND"还是用"OR"操作逻辑),一般默认指定。
3) df:默认的查询字段,一般默认指定。
4) qt:query type,指定查询使用的QueryHandler,默认为“standard”。
5) wt:writer type。指定查询输出结构格式,默认为“xml”。在solrconfig.xml中定义了查询输出格式:xml、json、python、ruby、php、phps、custom。
6) echoHandler:是否在查询结果中显示使用的Query Handler名称。
7) echoParams:是否显示查询参数。none:不显示;explicit:只显示查询参数;all:所有,包括在solrconfig.xml定义的Query Handler参数。
8) indent- 返回的结果是否缩进,默认关闭,用 indent=true|on 开启,一般调试json,php,phps,ruby输出才有必要用这个参数。
9) version- 查询语法的版本,建议不使用它,由服务器指定默认值。
2. CommonQueryParameters
1) sort:排序,格式:sort=<fieldname>+<desc|asc>[,<field name>+<desc|asc>]… 。示例:(inStock desc, price asc)表示先“inStock” 降序, 再 “price” 升序,默认是相关性降序。。
2) start:用于分页定义结果起始记录数,默认为0。
3) rows:用于分页定义结果每页返回记录数,默认为10。
4) fq:filter query。使用Filter Query可以充分利用Filter Query Cache,提高检索性能。作用:在q查询符合结果中同时是fq查询符合的,例如:q=mm&fq=date_time:[20081001TO 20091031],找关键字mm,并且date_time是20081001到20091031之间的。
5) fl:field list。指定返回结果字段。以空格“ ”或逗号“,”分隔。
6) debugQuery:设置返回结果是否显示Debug信息。
7) explainOther:设置当debugQuery=true时,显示其他的查询说明。
8) defType:设置查询解析器名称。
9) timeAllowed:设置查询超时时间。
10) omitHeader:设置是否忽略查询结果返回头信息,默认为“false”。
4.7.2查询语法
1. 匹配所有文档:*:*
2. 强制、阻止和可选查询:
1) Mandatory:查询结果中必须包括的(for example, only entryname containing the word make)
Solr/Lucene Statement:+make, +make +up ,+make +up +kiss
2) prohibited:(for example, all documents exceptthose with word believe)
Solr/Lucene Statement:+make +up -kiss
3) optional:
Solr/Lucene Statement:+make +up kiss
3. 布尔操作:AND、OR和NOT布尔操作(必须大写)与Mandatory、optional和prohibited相似。
1) makeAND up = +make +up:AND左右两边的操作都是mandatory
2) make|| up = make OR up=make up :OR左右两边的操作都是optional
3) +make+up NOT kiss = +make +up –kiss
4) makeAND up OR french AND Kiss不可以达到期望的结果,因为AND两边的操作都是mandatory的。
5)FLOW_TEMPLATE_ID:(* NOTcom.asiainfo.cs.sr.vm.ComplaintWorkFlow_01 NOTcom.asiainfo.cs.kb.vm.KbDocRequest_01)
4. 子表达式查询(子查询):可以使用“()”构造子查询。
For ex:(make AND up) OR (french AND Kiss)
OR 或以下三种写法结果一致,三种不同的方式 (OR 必须大写)
assertEquals(2,server.query(newSolrQuery("id:(001 OR 003)")).getResults().size());
assertEquals(2,server.query(newSolrQuery("id:(001 || 003)")).getResults().size());
assertEquals(2,server.query(newSolrQuery("id:001 || id:003")).getResults().size());
5. 子表达式查询中阻止查询的限制:
For ex:make (-up):只能取得make的查询结果;要使用make (-up *:*)查询make或者不包括up的结果。
6. 多字段fields查询:通过字段名加上分号的方式(fieldName:query)来进行查询
For ex:entryNm:make AND entryId:3cdc86e8e0fb4da8ab17caed42f6760c
7. 通配符查询(wildCard Query):
1) 通配符?和*:“*”表示匹配任意字符;“?”表示匹配出现的位置。
For ex:ma?*(ma后面的一个位置匹配),ma??*(ma后面两个位置都匹配)
2) 查询字符必须要小写:+Ma +be**可以搜索到结果;+Ma +Be**没有搜索结果
3) 查询速度较慢,尤其是通配符在首位:主要原因一是需要迭代查询字段中的每个term,判断是否匹配;二是匹配上的term被加到内部的查询,当terms数量达到1024的时候,查询会失败。
4) Solr中默认通配符不能出现在首位(可以修改QueryParser,设置
setAllowLeadingWildcard为true)
5) setsetAllowLeadingWildcard to true.
8. 模糊查询、相似查询:不是精确的查询,通过对查询的字段进行重新插入、删除和转换来取得得分较高的查询解决(由Levenstein Distance Algorithm算法支持)。
1) 一般模糊查询:for ex:make-believ~
2) 门槛模糊查询:对模糊查询可以设置查询门槛,门槛是0~1之间的数值,门槛越高表面相似度越高。For ex:make-believ~0.5、make-believ~0.8、make-believ~0.9
9. 范围查询(Range Query):Lucene支持对数字、日期甚至文本的范围查询。结束的范围可以使用“*”通配符。
For ex:
1) 日期范围(ISO-8601 时间GMT):sa_type:2 AND a_begin_date:[1990-01-01T00:00:00.000Z TO1999-12-31T24:59:99.999Z]
2) 数字:salary:[2000 TO *]
3) 文本:entryNm:[a TO a]
10. 日期匹配:YEAR, MONTH, DAY, DATE(synonymous with DAY) HOUR, MINUTE, SECOND, MILLISECOND, and MILLI (synonymouswith MILLISECOND)可以被标志成日期。
For ex:
1) r_event_date:[*TO NOW-2YEAR]:2年前的现在这个时间
2) r_event_date:[*TO NOW/DAY-2YEAR]:2年前前一天的这个时间
[]表示查询一个日期范围,包括边界即createtime>=a and createtime <=b。{}表示查询一个日期范围不包括边界即createtime>a and createtime <b。
A TO * 表示没有上界即>=A或是>A ,视使用的是[]还是{}而定
* TO A 表示没有下界即<=A或是<A ,视使用的是[]还是{}而定
NOW表示当前时间,NOW/DAY表示当前日期.
支持运算,比如取前一天就是NOW/DAY-1DAY,后一天就是NOW/DAY+1DAY,前年NOW-1YEAR ,依次类推。
11、转义字符
Solr能接受的写法
assertEquals("Artist:20",server.query(new SolrQuery("id:\\Artist:20")).getResults().get(0).getFieldValue("id"));
assertEquals("A:B:C&*FD",server.query(newSolrQuery("name:A\\:B\\:C\\&\\*FD")).getResults().get(0).getFieldValue("name"));
Solrj提供了工具类对内容进行转义ClientUtils.escapeQueryChars(String s);
+ - && || ! ( ) { } [ ] ^ " ~* ? : \ 这些字符必须要转义
assertEquals("Artist:20",server.query(newSolrQuery("id:"+ClientUtils.escapeQueryChars("Artist:20"))).getResults().get(0).getFieldValue("id"));
assertEquals("A:B:C&*FD",server.query(new SolrQuery("name:"+ClientUtils.escapeQueryChars("A:B:C&*FD"))).getResults().get(0).getFieldValue("name"));
4.7.3函数查询(Function Query)
函数查询 可以利用 numeric域的值 或者 与域相关的的某个特定的值的函数,来对文档进行评分。
1. 使用函数查询的方法
这里主要有三种方法可以使用函数查询,这三种s方法都是通过solrhttp接口的。
1) 使用FunctionQParserPlugin。ie: q={!func}log(foo)
2) 使用“_val_”内嵌方法
内嵌在正常的solr查询表达式中。即,将函数查询写在 q这个参数中,这时候,我们使用“_val_”将函数与其他的查询加以区别。
ie:entryNm:make && _val_:ord(entryNm)
3) 使用dismax中的bf参数
使用明确为函数查询的参数,比如说dismax中的bf(boost function)这个参数。 注意:bf这个参数是可以接受多个函数查询的,它们之间用空格隔开,它们还可以带上权重。所以,当我们使用bf这个参数的时候,我们必须保证单个函数中是没有空格出现的,不然程序有可能会以为是两个函数。
For ex:
q=dismax&bf="ord(popularity)^0.5recip(rord(price),1,1000,1000)^0.3
2. 函数的格式(Function Query Syntax)
目前,functionquery 并不支持 a+b 这样的形式,我们得把它写成一个方法形式,这就是 sum(a,b).
3. 使用函数查询注意事项
1) 用于函数查询的field必须是被索引的;
2) 字段不可以是多值的(multi-value)
4. 可以利用的函数 (available function)
1) constant:支持有小数点的常量; 例如:1.5 ;SolrQuerySyntax:_val_:1.5
2) fieldvalue:这个函数将会返回numeric field的值,这个域必须是indexd的,非multiValued的。格式很简单,就是该域的名字。如果这个域中没有这样的值,那么将会返回0。
3) ord:对于一个域,它所有的值都将会按照字典顺序排列,这个函数返回你要查询的那个特定的值在这个顺序中的排名。这个域,必须是非multiValued的,当没有值存在的时候,将返回0。例如:某个特定的域只能去三个值,“apple”、“banana”、“pear”,那么ord(“apple”)=1,ord(“banana”)=2,ord(“pear”)=3.需要注意的是,ord()这个函数,依赖于值在索引中的位置,所以当有文档被删除、或者添加的时候,ord()的值就会发生变化。当你使用MultiSearcher的时候,这个值也就是不定的了。
4) rord:这个函数将会返回与ord相对应的倒排序的排名。
格式:rord(myIndexedField)。
5) sum:这个函数的意思就显而易见啦,它就是表示“和”啦。
格式:sum(x,1) 、sum(x,y)、 sum(sqrt(x),log(y),z,0.5)
6) product:product(x,y,...)将会返回多个函数的乘积。格式:product(x,2)、product(x,y)
7) div:div(x,y)表示x除以y的值,格式:div(1,x)、div(sum(x,100),max(y,1))
8) pow:pow表示幂值。pow(x,y) =x^y。例如:pow(x,0.5) 表示开方pow(x,log(y))
9) abs:abs(x)将返回表达式的绝对值。格式:abs(-5)、 abs(x)
10) log:log(x)将会返回基数为10,x的对数。格式: log(x)、 log(sum(x,100))
11) Sqrt:sqrt(x) 返回 一个数的平方根。格式:sqrt(2)、sqrt(sum(x,100))
12) Map:如果 x>=min,且x<=max,那么map(x,min,max,target)=target.如果 x不在[min,max]这个区间内,那么map(x,min,max,target)=x.
格式:map(x,0,0,1)
13) Scale:scale(x,minTarget,maxTarget) 这个函数将会把x的值限制在[minTarget,maxTarget]范围内。
14) query:query(subquery,default)将会返回给定subquery的分数,如果subquery与文档不匹配,那么将会返回默认值。任何的查询类型都是受支持的。可以通过引用的方式,也可以直接指定查询串。
例子:q=product(popularity,query({!dismax v='solr rocks'}) 将会返回popularity和通过dismax 查询得到的分数的乘积。
q=product(popularity,query($qq)&qq={!dismax}solr rocks 跟上一个例子的效果是一样的。不过这里使用的是引用的方式
q=product(popularity,query($qq,0.1)&qq={!dismax}solr rocks 在前一个例子的基础上又加了一个默认值。
15) linear: inear(x,m,c)表示 m*x+c ,其中m和c都是常量,x是一个变量也可以是一个函数。例如:linear(x,2,4)=2*x+4.
16) Recip:recip(x,m,a,b)=a/(m*x+b)其中,m、a、b是常量,x是变量或者一个函数。当a=b,并且x>=0的时候,这个函数的最大值是1,值的大小随着x的增大而减小。例如:recip(rord(creationDate),1,1000,1000)
17) Max: max(x,c)将会返回一个函数和一个常量之间的最大值。
例如:max(myfield,0)
4.8 接口封装
采用adapter模式对SolrCloud的代码进行适配,采用java的反射实现对错误日志的处理,并通过表配置实现服务端地址和超时控制。
1. 配置AICS.CS_CFG_SOLR表:该表租户分表
数据示例:
2. 公共代码:
代码统一放置在:base目录下面:
其中:java/com/asiainfo/cs/base/common/solr/ISolrCloudServerAdapter.java
对最常用的10个接口进行了适配。
3. 客户端调用:
仅仅需要一行代码即可!
SolrCloudClient.getSolrCloudServer(ConfigConstants.SOLR_COLLECTION_NAME_TROUBLE_TICKET/*{collection_sr_ticket}*/).add(doc);
4. 错误日志记录表
当发生错误时,错误数据将存放到下面表中。
第5章 优化
SOLR优化共三部分,第一部分Solr常规处理,第二部分针对性性处理,前者比较通用,后者有局限性。务必根据具体应用特性,具体调节参数,对比性能。第三部solr查询相关的具体应用需要全面去把控,各个因素一起起作用。
Schema Design Considerations
Ø indexed fields
indexed fields 的数量将会影响以下的一些性能:
索引时的时候的内存使用量
索引段的合并时间
优化时间
索引的大小
们可以通过将omitNorms=“true”来减少indexed fields数量增加所带来的影响。
Ø stored fields
Retrieving the stored fields 确实是一种开销。这个开销,受每个文档所存储的字节影响很大。每个文档的所占用的空间越大,文档就显的更稀疏,这样从硬盘中读取数据,就需要更多的i/o操作(通常,我们在存储比较大的域的时候,就会考虑这样的事情,比如存储一篇文章的文档。)
可以考虑将比较大的域放到solr外面来存储。如果你觉得这样做会有些别扭的话,可以考虑使用压缩的域,但是这样会加重cpu在存储和读取域的时候的负担。不过这样却是可以较少i/0的负担。
如果,你并不是总是使用storedfields的话,可以使用storedfield的延迟加载,这样可以节省很多的性能,尤其是使用compressed field 的时候。
Configuration Considerations
Ø mergeFactor
这个是合并因子,这个参数大概决定了segment(索引段)的数量。
合并因子这个值告诉lucene,在什么时候,要将几个segment合并成为一个segment, 合并因子就像是一个数字系统的基数一样。
比如说,如果你将合并因子设成10,那么每往索引中添加1000个文档的时候,就会创建一个新的索引段。当第10个大小为1000的索引段添加进来的时候,这十个索引段就会被合并成一个大小为10,000的索引段。当十个大小为10,000的索引段生成的时候,它们就会被合并成一个大小为100,000的索引段。如此类推下去。这个值可以在solrconfig.xml 中的*mainIndex*中设置。(不用管indexDefaults中设置)
Ø mergeFactor Tradeoffs
较高的合并因子, 会提高索引速度,
较低频率的合并,会导致更多的索引文件,这会降低索引的搜索效率
较低的合并因子
较少数量的索引文件,能加快索引的搜索速度。
较高频率的合并,会降低索引的速度。
Ø HashDocSet Max Size Considerations
hashDocSet是solrconfig.xml中自定义优化选项, 使用在filters(docSets) 中,更小的sets,表明更小的内存消耗、遍历、插入。
hashDocSet参数值最后基于索引文档总数来定,索引集合越大,hashDocSet值也越大。
Ø Cache autoWarm Count Considerations
当一个新的searcher 打开的时候,它缓存可以被预热,或者说使用从旧的searcher的缓存的数据来“自动加热”。autowarmCount是这样的一个参数,它表示从旧缓存中拷贝到新缓存中的对象数量。autowarmCount这个参数将会影响“自动预热”的时间。有些时候,我们需要一些折中的考虑,seacher启动的时间和缓存加热的程度。当然啦,缓存加热的程度越好,使用的时间就会越长,但往往,我们并不希望过长的seacher启动时间。这个autowarm 参数可以在solrconfig.xml文件中被设置。
Ø Cache hit rate(缓存命中率)
我们可以通过solr的admin界面来查看缓存的状态信息。提高solr缓存的大小往往是提高性能的捷径。当你使用面搜索的时候,你或许可以注意一下filterCache,这个是由solr实现的缓存。
Ø Explicit Warming of Sort Fields
如果你有许多域是基于排序的,那么你可以在“newSearcher”和“firstSearcher”eventlisteners中添加一些明显需要预热的查询,这样FieldCache 就会缓存这部分内容。
Ø Optimization Considerations
优化索引,是我们经常会做的事情,比如,当我们建立好索引,然后这个索引不会再变更的情况,我们就会做一次优化了。但,如果你的索引经常会改变,那么你就需要好好的考虑下面的因素的。
n 当越来越多的索引段被加进索引,查询的性能就会降低, lucene对索引段的数量有一个上限的限制,当超过这个限制的时候,索引段可以自动合并成为一个。
n 在同样没有缓存的情况下,一个没有经过优化的索引的性能会比经过优化的索引的性能少10%……
n 自动加热的时间将会变长,因为它依赖于搜索。
n 优化将会对索引的分发产生影响。
n 在优化期间,文件的大小将会是索引的两倍,不过最终将会回到它原来的大小,或者会更小一点。
优化,会将所有的索引段合并成为一个索引段,所以,优化这个操作其实可以帮助避免“too many files”这个问题,这个错误是由文件系统抛出的。
Ø Updates and Commit Frequency Tradeoffs
如果从机 经常从 主机更新的话,从机的性能是会受到影响的。为了避免,由于这个问题而引起的性能下降,我们还必须了解从机是怎样执行更新的,这样我们才能更准确去调节一些相关的参数(commit的频率,spappullers, autowarming/autocount),这样,从机的更新才不会太频繁。
1. 执行commit操作会让solr新生成一个snapshot。如果将postCommit参数设成true的话,optimization也会执行snapShot.
2. slave上的Snappuller程序一般是在crontab上面执行的,它会去master询问,有没有新版的snapshot。一旦发现新的版本,slave就会把它下载下来,然后snapinstall.
3. 每次当一个新的searcher被open的时候,会有一个缓存预热的过程,预热之后,新的索引才会交付使用。
这里讨论三个有关的参数:
Ø number/frequency of snapshots —-snapshot的频率。
Ø snappullers 是 在crontab中的,它当然可以每秒一次、每天一次、或者其他的时间间隔一次运行。它运行的时候,只会下载slave上没有的,并且最新的版本。
Ø Cache autowarming 可以在solrconfig.xml文件中配置。
如果,你想要的效果是频繁的更新slave上的索引,以便这样看起来比较像“实时索引”。那么,你就需要让snapshot尽可能频繁的运行,然后也让snappuller频繁的运行。这样,我们或许可以每5分钟更新一次,并且还能取得不错的性能,当然啦,cach的命中率是很重要的,恩,缓存的加热时间也将会影响到更新的频繁度。
Ø cache对性能是很重要的。
一方面,新的缓存必须拥有足够的缓存量,这样接下来的的查询才能够从缓存中受益。另一方面,缓存的预热将可能占用很长一段时间,尤其是,它其实是只使用一个线程,和一个cpu在工作。snapinstaller太频繁的话,solr
slave将会处于一个不太理想的状态,可能它还在预热一个新的缓存,然而一个更新的searcher被opern了。怎么解决这样的一个问题呢,我们可能会取消第一个seacher,然后去处理一个更新seacher,也即是第二个。然而有可能第二个seacher 还没有被使用上的时候,第三个又过来了。看吧,一个恶性的循环,不是。当然也有可能,我们刚刚预热好的时候就开始新一轮的缓存预热,其实,这样缓存的作用压根就没有能体现出来。出现这种情况的时候,降低snapshot的频率才是硬道理。
Ø Query Response Compression
在有些情况下,我们可以考虑将solr xml response 压缩后才输出。如果response非常大,就会触及NIc i/o限制。
当然压缩这个操作将会增加cpu的负担,其实,solr一个典型的依赖于cpu处理速度的服务,增加这个压缩的操作,将无疑会降低查询性能。但是,压缩后的数据将会是压缩前的数据的6分之一的大小。然而solr的查询性能也会有15%左右的消耗。至于怎样配置这个功能,要看你使用的什么服务器而定,可以查阅相关的文档。
Ø Embedded vs HTTP Post
使用embeded来建立索引,将会比使用xml格式来建立索引快50%。
RAM Usage Considerations(内存方面的考虑)
Ø OutOfMemoryErrors
如果你的solr实例没有被指定足够多的内存的话,java virtual machine也许会抛outof memoryError,这个并不对索引数据产生影响。但是这个时候,任何的adds/deletes/commits操作都是不能够成功的。
Ø Memory allocated to the Java VM
最简单的解决这个方法就是,当然前提是java virtual machine还没有使用掉你全部的内存,增加运行solr的java虚拟机的内存。
Ø Factors affecting memory usage(影响内存使用量的因素)
我想,你或许也会考虑怎样去减少solr的内存使用量。其中的一个因素就是input document的大小。当我们使用xml执行add操作的时候,就会有两个限制。document中的field都是会被存进内存的,field有个属性叫maxFieldLength,它或许能帮上忙。每增加一个域,也是会增加内存的使用的。
第6章 运维
6.1 ZK日常运维
6.1.1ZK清理数据目录
在使用zookeeper过程中,我们知道,会有dataDir和dataLogDir两个目录,分别用于snapshot和事务日志的输出(默认情况下只有dataDir目录,snapshot和事务日志都保存在这个目录中。
正常运行过程中,ZK会不断地把快照数据和事务日志输出到这两个目录,并且如果没有人为操作的话,ZK自己是不会清理这些文件的,需要管理员来清理,这里介绍4种清理日志的方法。
社区反映的两个问题:
https://issues.apache.org/jira/browse/ZOOKEEPER-957
http://zookeeper-user.578899.n2.nabble.com/PurgeTxnLog-td6304244.html
第一种,也是运维人员最常用的,写一个删除日志脚本,每天定时执行即可:
#!/bin/bash
#snapshot file dir
dataDir=/home/yinshi.nc/test/zk_data/version-2
#tran log dir
dataLogDir=/home/yinshi.nc/test/zk_log/version-2
#zk log dir
logDir=/home/yinshi.nc/test/logs
#Leave 66 files
count=66
count=$[$count+1]
ls -t $dataLogDir/log.* | tail -n +$count | xargs rm -f
ls -t $dataDir/snapshot.* | tail -n +$count | xargs rm -f
ls -t $logDir/zookeeper.log.* | tail -n +$count | xargs rm -f
#find /home/yinshi.nc/taokeeper/zk_data/version-2 -name"snap*" -mtime +1 | xargs rm -f
#find /home/yinshi.nc/taokeeper/zk_logs/version-2 -name"log*" -mtime +1 | xargs rm -f
#find /home/yinshi.nc/taokeeper/logs/ -name"zookeeper.log.*" -mtime +1 | xargs rm –f
以上这个脚本定义了删除对应两个目录中的文件,保留最新的66个文件,可以将他写到crontab中,设置为每天凌晨2点执行一次就可以了。
第二种,使用ZK的工具类PurgeTxnLog。
它的实现了一种简单的历史文件清理策略,可以在这里看一下他的使用方法:http://zookeeper.apache.org/doc/r3.4.3/api/index.html,可以指定要清理的目录和需要保留的文件数目,简单使用如下:
java -cp zookeeper.jar:lib/slf4j-api-1.6.1.jar:lib/slf4j-log4j12-1.6.1.jar:lib/log4j-1.2.15.jar:conforg.apache.zookeeper.server.PurgeTxnLog < span>dataDir<<span>snapDir< -n < span>count<
第三种,对于上面这个Java类的执行,ZK自己已经写好了脚本,在bin/zkCleanup.sh中,所以直接使用这个脚本也是可以执行清理工作的。
【推荐,也是目前我们现在使用方式,直接使用zookeeper缺省设置即可】第四种,从3.4.0开始,zookeeper提供了自动清理snapshot和事务日志的功能,通过配置 autopurge.snapRetainCount 和autopurge.purgeInterval 这两个参数能够实现定时清理了。这两个参数都是在zoo.cfg中配置如下:
autopurge.purgeInterval 这个参数指定了清理频率,单位是小时,需要填写一个1或更大的整数,默认是0,表示不开启自己清理功能。
autopurge.snapRetainCount 这个参数和上面的参数搭配使用,这个参数指定了需要保留的文件数目。默认是保留3个。
下面官方解释:
autopurge.snapRetainCount
(No Java system property)
New in 3.4.0: When enabled, ZooKeeper auto purgefeature retains the autopurge.snapRetainCount most recentsnapshots and the corresponding transaction logs in the dataDir and dataLogDir respectivelyand deletes the rest. Defaults to 3. Minimum value is 3.
autopurge.purgeInterval
(No Java system property)
New in 3.4.0: The time interval in hours for whichthe purge task has to be triggered. Set to a positive integer (1 and above) toenable the auto purging. Defaults to 0.
6.1.2ZK程序日志
这里说两点,ZK默认是没有向ROLLINGFILE文件输出程序运行时日志的,需要我们自己在conf/log4j.properties中配置日志路径。另外,没有特殊要求的话,日志级别设置为INFO或以上,我曾经测试过,日志级别设置为DEBUG的话,性能影响很大!
6.2 ZK Server的自检恢复
ZK运行过程中,如果出现一些无法处理的异常,会直接退出进程,也就是所谓的快速失败(fail fast)模式。在上文中有提到,“过半存活即可用”的特性使得集群中少数机器down掉后,整个集群还是可以对外正常提供服务的。另外,这些down掉的机器重启之后,能够自动加入到集群中,并且自动和集群中其它机器进行状态同步(主要就是从Leader那里同步最新的数据),从而达到自我恢复的目的。
因此,我们很容易就可以想到,是否可以借助一些工具来自动完成机器的状态检测与重启工作。回答是肯定的,这里推荐两个工具: Daemontools(http://cr.yp.to/daemontools.html) 和 SMF(http://en.wikipedia.org/wiki/Service_Management_Facility),能够帮助你监控ZK进程,一旦进程退出后,能够自动重启进程,从而使down掉的机器能够重新加入到集群中去。
6.3 ZK监控
有几种方法:
1、 ZK提供一些简单但是功能强大的4字命令,通过对这些4字命令的返回内容进行解析,可以获取不少关于ZK运行时的信息。
2、用jmx也能够获取一些运行时信息,详细可以查看这里:http://zookeeper.apache.org/doc/r3.4.3/zookeeperJMX.html
3、淘宝网已经实现的一个ZooKeeper监控——TaoKeeper,已开源,在这里:http://rdc.taobao.com/team/jm/archives/1450,主要功能如下:
A、机器CPU/MEM/LOAD的监控
B、ZK日志目录所在磁盘空间监控
C、单机连接数的峰值报警
D、单机Watcher数的峰值报警
E、节点自检
F、ZK运行时信息展示
6.4 日志管理
ZK使用log4j作为日志系统,conf目录中有一份默认的log4j配置文件,注意,这个配置文件中还没有开启ROLLINGFILE文件输出,配置下即可。其它关于log4j的详细介绍,可以移步到log4j的官网:http://logging.apache.org/log4j/1.2/manual.html#defaultInit
6.4.1Solr日志
查看tomcat/logs目录下的日志。
1、查看tomcat是否成功启动、启动时长
tail -100f catalina.yyyy-mm-dd.log
2、查看solr相关日志,该日志按天输出一个,最大10个文件
tail -100f solr.log
3、查看所有日志,包括启动solr时输出的日志。
tail -100f catalina.out
4、 solradmin查看日志
日志在界面上会自动刷新。
6.4.2ZK日志
如果没有配置log4j.properties文件,日志会默认会在zk的bin目录下生成一个zookeeper.out的文件。该文件会很大,不方便查看。
需要调整zk输出日志方式:
1、 zk安装目录下面创建logs目录
2、 修改日志输出模式
a) 注释掉log4j.rootLogger=${zookeeper.root.logger}
b) 打开:log4j.rootLogger=INFO, CONSOLE, ROLLINGFILE
3、 在{zk安装目录}conf/log4j.properties,注释掉老的输出路径,新增新的输出路径,如下:
#log4j.appender.ROLLINGFILE.File=${zookeeper.log.dir}/${zookeeper.log.file}
log4j.appender.ROLLINGFILE.File=../logs/${zookeeper.log.file}
6.4.3汇总日志
举例:开发环境:/disk2/aidev/logs,测试环境:/home/solrtest/logs
为了方便查看日志,创建了单独zk和solr统一日志目录,通过linuxln命令与实际的日志文件建立的软链接。
日志文件包括:
solrtest/logs/solr1.log、solr2.log、solr3.log、solr4.log针对每个solr实例一个日志输出。
solrtest/logs/zk1.log、zk2.log、zk3.log针对每个zk实例一个日志输出。
solrall.log是所有solr实例日志的汇总日志文件。
zkall.log是所有zookeeper实例日志的汇总日志文件。
6.5 ZK加载数据出错
ZK在启动的过程中,首先会根据事务日志中的事务日志记录,从本地磁盘加载最后一次提交时候的快照数据,如果读取事务日志出错或是其它问题(通常在日志中可以看到一些IO异常),将导致server将无法启动。碰到类似于这种数据文件出错导致无法启动服务器的情况,一般按照如下顺序来恢复:
1、确认集群中其它机器是否正常工作,方法是使用“stat”这个命令来检查:echostat|nc ip 2181
2、如果确认其它机器是正常工作的(这里要说明下,所谓正常工作还是指集群中有过半机器可用),那么可以开始删除本机的一些数据了,删除$dataDir/version-2和$dataLogDir/version-2 两个目录下的所有文件。
重启server。重启之后,这个机器就会从Leader那里同步到最新数据,然后重新加入到集群中提供服务。
6.6 ZK配置参数详解
主要是%ZOOKEEPER_HOME%/conf/zoo.cfg文件
参数名 |
说明 |
clientPort |
客户端连接server的端口,即对外服务端口,一般设置为2181吧。 |
dataDir |
存储快照文件snapshot的目录。默认情况下,事务日志也会存储在这里。建议同时配置参数dataLogDir, 事务日志的写性能直接影响zk性能。 |
tickTime |
ZK中的一个时间单元。ZK中所有时间都是以这个时间单元为基础,进行整数倍配置的。例如,session的最小超时时间是2*tickTime。 |
dataLogDir |
事务日志输出目录。尽量给事务日志的输出配置单独的磁盘或是挂载点,这将极大的提升ZK性能。 (No Java system property) |
globalOutstandingLimit |
最大请求堆积数。默认是1000。ZK运行的时候, 尽管server已经没有空闲来处理更多的客户端请求了,但是还是允许客户端将请求提交到服务器上来,以提高吞吐性能。当然,为了防止Server内存溢出,这个请求堆积数还是需要限制下的。 (Java system property:?zookeeper.globalOutstandingLimit.) |
preAllocSize |
预先开辟磁盘空间,用于后续写入事务日志。默认是64M,每个事务日志大小就是64M。如果ZK的快照频率较大的话,建议适当减小这个参数。(Java system property:zookeeper.preAllocSize) |
snapCount |
每进行snapCount次事务日志输出后,触发一次快照(snapshot), 此时,ZK会生成一个snapshot.*文件,同时创建一个新的事务日志文件log.*。默认是100000.(真正的代码实现中,会进行一定的随机数处理,以避免所有服务器在同一时间进行快照而影响性能)(Java system property:zookeeper.snapCount) |
traceFile |
用于记录所有请求的log,一般调试过程中可以使用,但是生产环境不建议使用,会严重影响性能。(Java system property:requestTraceFile) |
maxClientCnxns |
单个客户端与单台服务器之间的连接数的限制,是ip级别的,默认是60,如果设置为0,那么表明不作任何限制。请注意这个限制的使用范围,仅仅是单台客户端机器与单台ZK服务器之间的连接数限制,不是针对指定客户端IP,也不是ZK集群的连接数限制,也不是单台ZK对所有客户端的连接数限制。指定客户端IP的限制策略,这里有一个patch,可以尝试一下:http://rdc.taobao.com/team/jm/archives/1334(No Java system property) |
clientPortAddress |
对于多网卡的机器,可以为每个IP指定不同的监听端口。默认情况是所有IP都监听clientPort指定的端口。New in 3.3.0 |
minSessionTimeoutmaxSessionTimeout |
Session超时时间限制,如果客户端设置的超时时间不在这个范围,那么会被强制设置为最大或最小时间。默认的Session超时时间是在2 * tickTime ~ 20 * tickTime这个范围New in 3.3.0 |
fsync.warningthresholdms |
事务日志输出时,如果调用fsync方法超过指定的超时时间,那么会在日志中输出警告信息。默认是1000ms。(Java system property:fsync.warningthresholdms) New in 3.3.4 |
autopurge.purgeInterval |
在上文中已经提到,3.4.0及之后版本,ZK提供了自动清理事务日志和快照文件的功能,这个参数指定了清理频率,单位是小时,需要配置一个1或更大的整数,默认是0,表示不开启自动清理功能。(No Java system property) New in 3.4.0 |
autopurge.snapRetainCount |
这个参数和上面的参数搭配使用,这个参数指定了需要保留的文件数目。默认是保留3个。(No Java system property) New in 3.4.0 |
electionAlg |
在之前的版本中, 这个参数配置是允许我们选择leader选举算法,但是由于在以后的版本中,只会留下一种“TCP-based version of fast leader election”算法,所以这个参数目前看来没有用了,这里也不详细展开说了。(No Java system property) |
initLimit |
Follower在启动过程中,会从Leader同步所有最新数据,然后确定自己能够对外服务的起始状态。Leader允许F在initLimit时间内完成这个工作。通常情况下,我们不用太在意这个参数的设置。如果ZK集群的数据量确实很大了,F在启动的时候,从Leader上同步数据的时间也会相应变长,因此在这种情况下,有必要适当调大这个参数了。(No Java system property) |
syncLimit |
在运行过程中,Leader负责与ZK集群中所有机器进行通信,例如通过一些心跳检测机制,来检测机器的存活状态。如果L发出心跳包在syncLimit之后,还没有从F那里收到响应,那么就认为这个F已经不在线了。注意:不要把这个参数设置得过大,否则可能会掩盖一些问题。(No Java system property) |
leaderServes |
默认情况下,Leader是会接受客户端连接,并提供正常的读写服务。但是,如果你想让Leader专注于集群中机器的协调,那么可以将这个参数设置为no,这样一来,会大大提高写操作的性能。(Java system property: zookeeper.leaderServes)。 |
server.x=[hostname]:nnnnn[:nnnnn] |
这里的x是一个数字,与myid文件中的id是一致的。右边可以配置两个端口,第一个端口用于F和L之间的数据同步和其它通信,第二个端口用于Leader选举过程中投票通信。 (No Java system property) |
group.x=nnnnn[:nnnnn]weight.x=nnnnn |
对机器分组和权重设置,可以 参见这里(No Java system property) |
cnxTimeout |
Leader选举过程中,打开一次连接的超时时间,默认是5s。(Java system property: zookeeper.cnxTimeout) |
zookeeper.DigestAuthenticationProvider .superDigest |
ZK权限设置相关,具体参见《使用super身份对有权限的节点进行操作》 和 《ZooKeeper权限控制》 |
skipACL |
对所有客户端请求都不作ACL检查。如果之前节点上设置有权限限制,一旦服务器上打开这个开头,那么也将失效。(Java system property:zookeeper.skipACL) |
forceSync |
这个参数确定了是否需要在事务日志提交的时候调用FileChannel.force来保证数据完全同步到磁盘。(Java system property:zookeeper.forceSync) |
jute.maxbuffer |
每个节点最大数据量,是默认是1M。这个限制必须在server和client端都进行设置才会生效。(Java system property:jute.maxbuffer) 默认为1024,单位KB,即表示节点可挂载的data最大为1M。如果修改此值,请首先确保所有的server上一致。 This option can only be set as a Java system property. There is no zookeeper prefix on it.It specifies the maximum size of the data that can be stored in a znode. The default is 0xfffff, or just under 1M. If this option is changed, the system property must be set on all servers and clients otherwise problems will arise. This is really a sanity check. ZooKeeper is designed to store data on the order of kilobytes in size. |
6.7 常用脚本
6.7.1Zookeeper脚本
功能 |
命令 |
描述 |
启动 |
|
|
重启 |
restartzookeeper.sh |
|
关闭 |
shutdownzookeeper.sh |
|
查询状态 |
|
6.7.2Tomcat脚本
功能 |
命令 |
描述 |
启动 |
starttomcat.sh |
|
关闭 |
shutdowntomcat.sh |
|
检查是否正常启动 |
|
6.7.3日志脚本
功能 |
命令 |
描述 |
输出所有solr日志 |
startsolralllog.sh |
tail -f solr*.log > solrall.log & 其中&表示从后台运行 |
输出所有zk日志 |
startzkalllog.sh |
tail -f zk*.log > solrall.log & |
6.7.4部署脚本
功能 |
命令 |
描述 |
启动所有tomcat |
/home/solrtest/tomcat_solr1_6801/bin/startup.sh /home/solrtest/tomcat_solr2_6802/bin/startup.sh /home/solrtest/tomcat_solr3_6803/bin/startup.sh /home/solrtest/tomcat_solr4_6804/bin/startup.sh |
|
删除所有solr应用 |
rm -rf /home/solrtest/tomcat_solr1_6801/webapps/solr* rm -rf /home/solrtest/tomcat_solr2_6802/webapps/solr* rm -rf /home/solrtest/tomcat_solr3_6803/webapps/solr* rm -rf /home/solrtest/tomcat_solr4_6804/webapps/solr* |
升级solr新版本使用 |
复制新的solr应用 |
cp /home/solrtest/software/solr-4.7.1.war /home/solrtest/tomcat_solr1_6801/webapps/solr.war cp /home/solrtest/software/solr-4.7.1.war /home/solrtest/tomcat_solr2_6802/webapps/solr.war cp /home/solrtest/software/solr-4.7.1.war /home/solrtest/tomcat_solr3_6803/webapps/solr.war cp /home/solrtest/software/solr-4.7.1.war /home/solrtest/tomcat_solr4_6804/webapps/solr.war |
|
6.8 常用命令
6.8.1ZK文件命令
功能 |
命令 |
upconfig |
上传配置文件 /home/solrtest/solrcloud/cloud-scripts/zkcli.sh -cmd upconfig -zkhost 10.11.20.107:2181,10.11.20.107:2182,10.11.20.107:2183 -confdir /home/solrtest/solrcloud/config-files/sr_ticket -confnamesr_ticket_03_conf |
linkconfig |
链接collection与配置文件 /home/solrtest/solrcloud/cloud-scripts/zkcli.sh -cmd linkconfig -collection sr_ticket_03 -confname sr_ticket_03_conf -zkhost 10.11.20.107:2181,10.11.20.107:2182,10.11.20.107:2183 |
zookeeper-3.4.5-1/bin/zkCli.sh -server 10.11.20.107:2181 |
|
查看ZK文件 |
ls / ls /configs ls /collections |
ZK命令 |
[zk: 10.11.20.106:2181(CONNECTED) 5] ls / [configs, zookeeper, clusterstate.json, aliases.json, live_nodes, overseer, collections, overseer_elect] [zk: 10.11.20.106:2181(CONNECTED) 6] more clusterstate.json ZooKeeper -server host:port cmd args connect host:port get path [watch] ls path [watch] set path data [version] rmr path delquota [-n|-b] path quit printwatches on|off create [-s] [-e] path data acl stat path [watch] close ls2 path [watch] history listquota path setAcl path acl getAcl path sync path redo cmdno addauth scheme auth delete path [version] setquota -n|-b val path [zk: 10.11.20.106:2181(CONNECTED) 7] [zk: 10.11.20.106:2181(CONNECTED) 7] ls2 /configs [sr_ticket_01_conf, kb_doc_request_02_conf, kb_prompts_21_conf, kb_doc_edit_01_conf, kb_doc_edit_02_conf, kb_article_03_conf, kb_doc_request_21_conf, kb_doc_edit_03_conf, kb_collection_02_conf, kb_article_01_conf, kb_collection_21_conf, kb_update_article_request_02_conf, kb_prompts_03_conf, kb_prompts_02_conf, kb_article_21_conf, kb_doc_request_01_conf, kb_article_02_conf, kb_new_article_request_02_conf, kb_prompts_01_conf, kb_collection_01_conf, kb_new_article_request_01_conf, kb_doc_request_03_conf, kb_doc_edit_21_conf, kb_update_article_request_01_conf, tm_work_item_02_conf, tm_work_item_01_conf, sr_ticket_02_conf, kb_collection_03_conf] cZxid = 0x200000088 ctime = Wed Mar 19 15:04:05 CST 2014 mZxid = 0x200000088 mtime = Wed Mar 19 15:04:05 CST 2014 pZxid = 0x16001ee1df cversion = 28 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 0 numChildren = 28 |
6.8.2ZK监控命令
源码:FourLetterWordMain.java
功能 |
命令 |
输出server简要状态和连接的客户端信息。 |
echo stat|nc localhost 2181
/10.11.20.107:48766[1](queued=0,recved=3076,sent=3180)
Latency min/avg/max: 0/15/2328 Received: 492590 Sent: 173861 Connections: 47 Outstanding: 0 Zxid: 0x300000b9a Mode: leader Node count: 989 |
输出一些ZK运行时信息,通过对这些返回结果的解析,可以达到监控的效果 |
echo mntr|nc localhost 2181
zk_version 3.4.5-1392090, built on 09/30/2012 17:52 GMT zk_avg_latency 14 zk_max_latency 2328 zk_min_latency 0 zk_packets_received 511550 zk_packets_sent 180445 zk_num_alive_connections 47 zk_outstanding_requests 0 zk_server_state leader zk_znode_count 989 zk_watch_count 94 zk_ephemerals_count 90 zk_approximate_data_size 3645317 zk_open_file_descriptor_count 76 zk_max_file_descriptor_count 20480 zk_followers 2 zk_synced_followers 2 zk_pending_syncs 0 |
输出server的详细配置信息 |
echo conf|nc localhost 2181
clientPort=2181 dataDir=/home/solrtest/solrcloud/zookeeper1/data/version-2 dataLogDir=/home/solrtest/solrcloud/zookeeper1/log/version-2 tickTime=2000 maxClientCnxns=200 minSessionTimeout=4000 maxSessionTimeout=40000 serverId=1 initLimit=10 syncLimit=5 electionAlg=3 electionPort=3888 quorumPort=2888 peerType=0 |
输出指定server上所有客户端连接的详细信息,包括客户端IP,会话ID等 |
echo cons|nc localhost 2181
/10.11.20.107:52356[1](queued=0,recved=2438,sent=2542, sid=0x144582599210103,lop=PING,est=1393048800634,to=5000, lcxid=0x6c,lzxid=0x300000b76, lresp=1393052759169,llat=0,minlat=0, avglat=11,maxlat=1207) …… |
重置所有连接的统计信息。 |
echo crst|nc localhost 2181 |
针对Leader执行,用于输出所有等待队列中的会话和临时节点的信息。 |
echo dump|nc localhost 2181 |
输出server的环境变量。包括操作系统环境和Java环境。 |
echo envi|nc localhost 2181 |
测试server是否处于无错状态。如果正常,则返回“imok”,否则没有任何响应。 |
echo ruok|nc localhost 2181 注意:ruok不是一个特别有用的命令,它不能反映一个server是否处于正常工作。“stat”命令更靠谱。 |
与上面功能类似 |
echo srvr|nc localhost 2181 |
重置server的统计信息 |
echo srst|nc localhost 2181 |
列出所有watcher信息概要信息,数量等 |
echo wchs|nc localhost 2181 |
列出所有watcher信息,以watcher的session为归组单元排列,列出该会话订阅了哪些path |
echo wchc|nc localhost 2181 |
列出所有watcher信息,以watcher的path为归组单元排列,列出该path被哪些会话订阅着 |
echo wchp|nc localhost 2181 |
6.8.3Tomcat命令
功能 |
命令 |
描述 |
查看tomcat进程 |
ps -ef|grep solr.solr.home |
|
Kill 进程 |
kill -9 进程编号
kill -9 进程编号1进程编号2进程编号3 |
有时tomcat启动中,此时需要停掉:shutdowntomcat.sh、shutdownzookeeper.sh无法正常关闭。需要kill进程。 |
6.8.4Solr新增数据
一、curl命令方式(推荐使用,这种方式最为简洁)
通过json方式:
curl "http://localhost:8983/solr/c2/update?commit=true" -H'Content-type:application/json' -d '[{"id":"a!doc1"},{"id":"b!doc2"},{"id":"c!doc3"},{"id":"d!doc4"}]'
通过csv方式:
curl http://localhost:8983/solr/update?commitWithin=5000 -H 'Content-type:text/csv' -d '
id,cat,pubyear_i,title,author,series_s,sequence_i
book1,fantasy,2000,A Storm of Swords,George R.R. Martin,A Song of Ice and Fire,3
book2,fantasy,2005,A Feast for Crows,George R.R. Martin,A Song of Ice and Fire,4
book3,fantasy,2011,A Dance with Dragons,George R.R. Martin,A Song of Ice and Fire,5
book4,sci-fi,1987,Consider Phlebas,Iain M. Banks,The Culture,1
book5,sci-fi,1988,The Player of Games,Iain M. Banks,The Culture,2
book6,sci-fi,1990,Use of Weapons,Iain M. Banks,The Culture,3
book7,fantasy,1984,Shadows Linger,Glen Cook,The Black Company,2
book8,fantasy,1984,The White Rose,Glen Cook,The Black Company,3
book9,fantasy,1989,Shadow Games,Glen Cook,The Black Company,4
book10,sci-fi,2001,Gridlinked,Neal Asher,Ian Cormac,1
book11,sci-fi,2003,The Line of Polity,Neal Asher,Ian Cormac,2
book12,sci-fi,2005,Brass Man,Neal Asher,Ian Cormac,3
'
二、通Solr admin来创建
6.8.5Solr删除数据命令
针对solr的索引,经常会需要针对已有的索引doc进行操作,而通过程序操作又不方便。我们希望能和我们通过sql客户端直接操作数据库一样方便来维护索引文档。
Solr admin为我们提供了方便操作doc的手段,针对我们日常常用的维护操作提供了一些实例。
一、curl命令方式(推荐使用,这种方式最为简洁)
1、删除数据:
(1)删除一个colllection的所有数据。(最为常用,特别是线上前,我们要清理所有流水表数据一样)
说明:向一个core/replica发起请求,会将该core对应的所对应collection全部清除,无需向不同的replica上发送多次,他们内部有同步策略。
格式:curlhttp://localhost:8983/solr/{corename}/update --data-binary"<delete><query>*:*</query></delete>" -H"Content-type:text/xml;charset=UTF-8"
举例:curl http://10.11.20.107:6801/solr/sr_ticket_01_shard2_replica1/update--data-binary"<delete><query>*:*</query></delete>" -H"Content-type:text/xml;charset=UTF-8"
(2)直接删除索引文件。不推荐使用,重启后才能生效。除非是新搭建环境,可以这样操作,其他情况不建议使用!
举例:删除投诉的所有索引数据:rm -rf /home/solrtest/solrcloud/solrhome*/sr_ticket_*/data/
rm -rf /home/solrtest/solrcloud/solrhome*/sr_*/data/*
rm -rf /home/solrtest/solrcloud/solrhome*/kb_*/data/*
(3)删除指定id数据:
举例:curlhttp://localhost:8983/solr/{corename}/update --data-binary"<delete><id>1</id></delete>" -H"Content-type:text/xml;charset=UTF-8"
二、通过post.jar来删除数据
1、删除数据:
举例:java -Ddata=args-Durl=http://localhost:8081/apache-solr-test/core0/update -jar post.jar"<delete><query>id:IW-02</query></delete>"
三、solr admin方式
选择指定的core的菜单中,选择Documents,在界面中Document Type选择不同值来完成不同操作。
举例1:删除数据。如果删除该core对应的collection的数据,先选择document type = xml,documents输入框中输入:<delete><query>*:*</query></delete>,然后点击提交。
举例2:增加数据。Documents默认打开就是待插入的测试数据。
6.8.6Solr Collection命令
命令 |
实例和说明 |
CREATE |
http://localhost:6801/solr/admin/collections?action=CREATE &name=sr_ticket_01&numShards=2 |
DELETE |
该删除操作后,会将zookeeper上配置文件都删除干净,如果重新创建collection前,需要重新上传和链接配置文件 http://localhost:6801/solr/admin/collections?action=DELETE&name=sr_ticket_01 |
RELOAD |
http://localhost:6801/solr/admin/collections?action=RELOAD&name=sr_ticket_01 |
CREATESHARD |
http://localhost:6801/solr/admin/collections?action=CREATESHARD &collection=sr_ticket_01&shard=shard-z1 |
SPLITSHARD |
http://localhost:6801/solr/admin/collections?action=SPLITSHARD &collection=sr_ticket_01&shard=shard |
DELETESHARD |
http://localhost:6801/solr/admin/collections?action=DELETESHARD &collection=sr_ticket_01&shard=shard1 |
CREATEALIAS |
http://localhost:6801/solr/admin/collections?action=CREATEALIAS &name=testalias&collections=sr_ticket_01,sr_ticket_02 |
创建replica |
http://10.11.20.107:6801/solr/admin/cores?action=CREATE &name=sr_ticket_01_shard1_replica1&collection=sr_ticket_01&shard=shard1 备注:本质上就是在指定的某个ip和port上进行创建一个core,创建完毕后会在10.11.20.107:6801上创建了名称为sr_ticket_01_shard1_replica1的文件夹。 |
DELETEREPLICA |
http://localhost:6801/solr/admin/collections?action=DELETEREPLICA &collection=sr_ticket_01&shard=shard2&replica=core_node3 5、 查询某个replica的名字。 可通过管理员界面中cloud/tree/clusterstate.json来查看json内容中:replicas下面一级的名字即为replica的名称,此时ip和port都有。 也直接在主机上查询配置文件core.properties中的名字。其中:replica为core.properties中的coreNodeName
6、 有时删除成功,但是有异常信息,可能是solr的bug,请直接忽略。 7、 注意每个http中的端口号务必与有问题solr instance对应。比如:某个10.11.20.107:6801端口的对应的IP为down状态,就需要向这个10.11.20.107:6801发起请求。 |
6.8.7Solr原子操作命令
Solr4.0开始支持简单的原子更新和添加字段
参数说明
Solr支持多种修饰符,自动更新文档的值。
set –更新一个字段
add –添加一个字段
inc –在原有值的基础上增加(看下面的例子更清楚)
备注: 所有原始字段必须存储(在fieldType 里面设置 stored=true)
操作例子
第一步、添加一条数据
$ curl http://localhost:8983/solr/update -H'Content-type:application/json' -d '
[
{"id" : "book1","title" : "Snow Crash",
"copies_i" : 5, "cat" : "Science Fiction"
}
]'
第二步、修改一个字段author,把copies_i字段的值加3,添加一个cat字段
$ curl http://localhost:8983/solr/update -H'Content-type:application/json' -d '
[
{"id" : "book1",
"author" :{"set":"Neal Stephenson"},
"copies_i" : {"inc":3},
"cat" :{"add":"Cyberpunk"}
}
]'
第三步、查询一下数据的情况
$ curlhttp://localhost:8983/solr/get?id=book1
{
“doc”: {
“id”:”book1″,
“title”:["SnowCrash"],
“copies_i”:8,
“cat”:["ScienceFiction", "Cyberpunk"],
“author”:”NealStephenson”,
“_version_”:1408729977723027456}}
6.9 文件管理
默认情况下,ZK的数据文件和事务日志是保存在同一个目录中,建议是将事务日志存储到单独的磁盘上。
6.9.1Solr配置文件
对于新增加的solrcollection配置文件,需要上传到solrcloud/config-files目录下。
6.9.2ZK数据目录
ZK的数据目录包含两类文件:
A、myid – 这个文件只包含一个数字,和server id对应。
B、snapshot. - 按zxid先后顺序的生成的数据快照。
集群中的每台ZK server都会有一个用于惟一标识自己的id,有两个地方会使用到这个id:myid文件和zoo.cfg文件中。myid文件存储在dataDir目录中,指定了当前server的server id。在zoo.cfg文件中,根据server id,配置了每个server的ip和相应端口。Zookeeper启动的时候,读取myid文件中的server id,然后去zoo.cfg 中查找对应的配置。
zookeeper在进行数据快照过程中,会生成 snapshot文件,存储在dataDir目录中。文件后缀是zxid,也就是事务id。(这个zxid代表了zk触发快照那个瞬间,提交的最后一个事务id)。注意,一个快照文件中的数据内容和提交第zxid个事务时内存中数据近似相同。仅管如此,由于更新操作的幂等性,ZK还是能够从快照文件中恢复数据。数据恢复过程中,将事务日志和快照文件中的数据对应起来,就能够恢复最后一次更新后的数据了。
6.9.3ZK事务日志目录
dataLogDir目录是ZK的事务日志目录,包含了所有ZK的事务日志。正常运行过程中,针对所有更新操作,在返回客户端“更新成功”的响应前,ZK会确保已经将本次更新操作的事务日志写到磁盘上,只有这样,整个更新操作才会生效。每触发一次数据快照,就会生成一个新的事务日志。事务日志的文件名是log.,zxid是写入这个文件的第一个事务id。
6.9.4ZK文件管理
不同的zookeeper server生成的snapshot文件和事务日志文件的格式都是一致的(无论是什么环境,或是什么样的zoo.cfg配置)。因此,如果某一天生产环境中出现一些古怪的问题,你就可以把这些文件下载到开发环境的zookeeper中加载起来,便于调试发现问题,而不会影响生产运行。另外,使用这些较旧的snapshot和事务日志,我们还能够方便的让ZK回滚到一个历史状态。
另外,ZK提供的工具类LogFormatter能够帮助可视化ZK的事务日志,帮助我们排查问题,关于事务日志的可以化,请查看这个文章《可视化zookeeper的事务日志》.
需要注意的一点是,zookeeper在运行过程中,不断地生成snapshot文件和事务日志,但是不会自动清理它们,需要管理员来处理。(ZK本身只需要使用最新的snapshot和事务日志即可)关于如何清理文件,上面章节“日常运维”有提到。
6.10 注意事项
6.10.1 保持Server地址列表一致
A、客户端使用的server地址列表必须和集群所有server的地址列表一致。(如果客户端配置了集群机器列表的子集的话,也是没有问题的,只是少了客户端的容灾。)
B、集群中每个server的zoo.cfg中配置机器列表必须一致。
6.10.2 独立的事务日志输出
对于每个更新操作,ZK都会在确保事务日志已经落盘后,才会返回客户端响应。因此事务日志的输出性能在很大程度上影响ZK的整体吞吐性能。强烈建议是给事务日志的输出分配一个单独的磁盘。
6.10.3 配置合理的JVM堆大小
确保设置一个合理的JVM堆大小,如果设置太大,会让内存与磁盘进行交换,这将使ZK的性能大打折扣。例如一个4G内存的机器的,如果你把JVM的堆大小设置为4G或更大,那么会使频繁发生内存与磁盘空间的交换,通常设置成3G就可以了。当然,为了获得一个最好的堆大小值,在特定的使用场景下进行一些压力测试。
第7章 问题和解答
7.1 开发和测试环境
北京开发地址为:http://10.1.195.241:6801/solr
测试地址为:http://10.11.20.107:6801/solr(随着版本发布会变化)
7.2 如何验证是否软件安装正确?
7.2.1验证Zookeeper
7.2.2验证Solr
检查项:
1、 输入Solr管理界面URL是否可正常打开。
2、 检查cloud节点的状态是否为active状态。举例:正确的发布的情况。
3、 选中某个节点进行查询没有报错。
4、 选中不同IP的URL检查是否可正常打开、并检查是否查询是报错,这样就可检查集群是否搭建成功。
7.3 生产环境部署关键检查项?
7.3.1Solr
日志级别配置为ERROR
7.3.2Zookeeper
1、 检查将zoo.cfg配置将自动清理snapshot和事务日志的功能。如何配置详细参见:ZK清理数据目录。
2、 检查日志log4j.properties配置是否正确,并检查是否输出到logs/目录下面。具体参见:ZK日志
3、 检查日志输出级别配置为ERROR
7.4 如何配置zookeeper日志输出目录?
如果没有配置log4j.properties文件,日志会默认会在zk的bin目录下生成一个zookeeper.out的文件。该文件会很大,不方便查看。
需要调整zk输出日志方式:
1、 zk安装目录下面创建logs目录。
2、 修改日志输出模式
a) 注释掉log4j.rootLogger=${zookeeper.root.logger}
b) 打开注释:log4j.rootLogger=INFO, CONSOLE, ROLLINGFILE,并删除掉CONSOLE
3、 在{zk安装目录}conf/log4j.properties,注释掉老的输出路径,新增新的输出路径,如下:
#log4j.appender.ROLLINGFILE.File=${zookeeper.log.dir}/${zookeeper.log.file}
log4j.appender.ROLLINGFILE.File=../logs/${zookeeper.log.file}
4、 打开注释:#log4j.appender.ROLLINGFILE.MaxBackupIndex=10
5、 修改${zkhome}/bin/zkEnv.sh,将ZOO_LOG4J_PROP="INFO,CONSOLE"修改为:ZOO_LOG4J_PROP="INFO,ROLLINGFILE",修改完毕后,仍会有zookeeper.out文件存在,但是文件大小为0。
7.5 如何查看日志?
1、 通过solr admin查看。
举例,你输入一个错误的查询条件,在日志输出平台中将会错误日志输出到屏幕中。
2、 查询后台日志文件。参见:日志管理
7.6 需要定时清理那些日志?
Tomcat、zookeeper的控制台日志会越来越大,需要定时清理。
1、 Tomcat日志:
a) 每个tomcat的catalina.out
b) 举例:/disk2/aidev/tomcat_solr1_6801/logs
2、 zk日志:
a) 可通过配置log4j.properties文件,自动清理日志。
3、 日志汇总目录中文件也需要清理
a) 清理:solrall.log、zkall.log这两个文件。
b) 举例:/disk2/aidev/logs/solrall.log、disk2/aidev/logs/zkall.log
7.7 如何在日志中定位关键字并输出到文件?
1、 grep –n keyword filename*.txt 支持在单个或多个文件,并可返回行号。
2、 比如定位到第200行,在通过sed -n '100,300p' filename >out.log,在该位置的前后分别加100行,也就是:输出从100行到300的日志;
7.8 新增scheme.xml,如何部署?
1、 在solrcloud/config-files/下面为新的collection创建文件夹。命令举例:mkdir tm_order_ticket
2、 先选择一个其他solrcloud/config-files/下面的已有collection目录,将所有的下面的文件,包括子目录文件全部复制到新创建的文件夹下。命令举例:cp -r sr_ticket/* tm_order_ticket/
3、 编辑scheme.xml。通过vi命令或ultraedit来编辑,在<!-- Special Ticket Field -->注释下面增加你要新增的元素。
4、 上传配置文件ZK和链接配置文件。如果存在多租户collection,则需要进行同样的操作。具体参见其他章节。上传zookeeper文件和链接collection
5、 创建collection和replica。具体参见其他章节。创建collection和replica
-- 需替换下面ip、collectionname
########tm_sff_ticket_21########updatezookeeper config########
~/solrcloud/cloud-scripts/zkcli.sh -cmdupconfig -zkhost 10.226.5.165:48600,10.226.5.165:48605,10.226.5.165:48610-confdir ~/solrcloud/config-files/tm_sff_ticket -confname tm_sff_ticket_21_conf
~/solrcloud/cloud-scripts/zkcli.sh -cmdlinkconfig -collection tm_sff_ticket_21 -confname tm_sff_ticket_21_conf -zkhost10.226.5.165:48600,10.226.5.165:48605,10.226.5.165:48610
########tm_sff_ticket_21########createcollection########
curl "http://10.226.5.165:48500/solr/admin/collections?action=CREATE&name=tm_sff_ticket_21&numShards=2&maxShardsPerNode=4&createNodeSet=10.226.5.165:48500_solr"
curl"http://10.226.5.165:48502/solr/admin/cores?action=CREATE&name=tm_sff_ticket_21_shard1_replica2&collection=tm_sff_ticket_21&shard=shard1"
curl"http://10.226.5.165:48504/solr/admin/cores?action=CREATE&name=tm_sff_ticket_21_shard1_replica3&collection=tm_sff_ticket_21&shard=shard1"
curl"http://10.226.5.165:48506/solr/admin/cores?action=CREATE&name=tm_sff_ticket_21_shard1_replica4&collection=tm_sff_ticket_21&shard=shard1"
curl"http://10.226.5.165:48502/solr/admin/cores?action=CREATE&name=tm_sff_ticket_21_shard2_replica2&collection=tm_sff_ticket_21&shard=shard2"
curl"http://10.226.5.165:48504/solr/admin/cores?action=CREATE&name=tm_sff_ticket_21_shard2_replica3&collection=tm_sff_ticket_21&shard=shard2"
curl"http://10.226.5.165:48506/solr/admin/cores?action=CREATE&name=tm_sff_ticket_21_shard2_replica4&collection=tm_sff_ticket_21&shard=shard2"
7.9 修改scheme.xml,如何升级?
scheme.xml文件字段变化类似与数据库表字段变化。
1、 首先修改配置文件。
solrcloud/config-files/下面对应自己collection下面的scheme.xml。
2、 上传ZK和链接配置文件。如果存在多租户collection,则需要进行同样的操作。
针对一个collection的操作实例:注意需要替换为对应环境的ip地址。同时如果是多租户需要对应修改租户标识,并对应每个租户执行一次。下面举例提供了两个租户的脚本:
/disk2/aidev/solrcloud/cloud-scripts/zkcli.sh -cmd upconfig -zkhost10.1.195.241:2181,10.1.195.241:2182,10.1.195.241:2183 -confdir/disk2/aidev/solrcloud/config-files/sr_ticket -confname sr_ticket_01_conf
/disk2/aidev/solrcloud/cloud-scripts/zkcli.sh -cmd linkconfig-collection sr_ticket_01 -confname sr_ticket_01_conf -zkhost10.1.195.241:2181,10.1.195.241:2182,10.1.195.241:2183
/disk2/aidev/solrcloud/cloud-scripts/zkcli.sh -cmd upconfig -zkhost10.1.195.241:2181,10.1.195.241:2182,10.1.195.241:2183 -confdir/disk2/aidev/solrcloud/config-files/sr_ticket -confname sr_ticket_02_conf
/disk2/aidev/solrcloud/cloud-scripts/zkcli.sh -cmd linkconfig-collection sr_ticket_02 -confname sr_ticket_02_conf -zkhost10.1.195.241:2181,10.1.195.241:2182,10.1.195.241:2183
3、 对collection进行reload操作。如果存在多租户collection,则需要进行同样的操作。
向一个租户对应的collection发起请求的实例:
curl"http://10.1.195.241:6801/solr/admin/collections?action=RELOAD&name=sr_ticket_01"
curl"http://10.1.195.241:6801/solr/admin/collections?action=RELOAD&name=sr_ticket_02"
4、 可登陆solr管理平台查询配置文件是否上传成功。
7.10 新增租户(即创建collection),如何操作?
新增一个租户,对solr来讲就是针对该租户增加一个的collection。类似与数据库新增该租户的表。
创建两个shard的实例:
1、 针对新collection上传ZK和链接配置文件。
-- 需替换下面的文件路径(/disk2/aidev)、ip、collectionname
-- 举例:新增一个投诉新租户,collection则命名为:tm_order_ticket_01、在ZK上的配置文件命名为:tm_order_ticket_01_conf
/disk2/aidev/solrcloud/cloud-scripts/zkcli.sh -cmd upconfig -zkhost10.1.195.241:2181,10.1.195.241:2182,10.1.195.241:2183 -confdir/disk2/aidev/solrcloud/config-files/tm_order_ticket -confnametm_order_ticket_01_conf
/disk2/aidev/solrcloud/cloud-scripts/zkcli.sh -cmd linkconfig-collection tm_order_ticket_01 -confname tm_order_ticket_01_conf -zkhost10.1.195.241:2181,10.1.195.241:2182,10.1.195.241:2183
2、创建collection和对应的replica
curl"http://10.1.195.241:6801/solr/admin/collections?action=CREATE&name=tm_order_ticket_01&numShards=2&createNodeSet=10.1.195.241:6801_solr,10.1.195.241:6802_solr"
下面的端口号根据上面执行完毕默认的端口号情况来调整下面的端口、replica名称,目的是将每个shard上的port分配均匀。
curl"http://10.1.195.241:6802/solr/admin/cores?action=CREATE&name=tm_order_ticket_01_shard1_replica2&collection=tm_order_ticket_01&shard=shard1"
curl"http://10.1.195.241:6803/solr/admin/cores?action=CREATE&name=tm_order_ticket_01_shard1_replica3&collection=tm_order_ticket_01&shard=shard1"
curl"http://10.1.195.241:6804/solr/admin/cores?action=CREATE&name=tm_order_ticket_01_shard1_replica4&collection=tm_order_ticket_01&shard=shard1"
curl"http://10.1.195.241:6801/solr/admin/cores?action=CREATE&name=tm_order_ticket_01_shard2_replica1&collection=tm_order_ticket_01&shard=shard2"
curl"http://10.1.195.241:6803/solr/admin/cores?action=CREATE&name=tm_order_ticket_01_shard2_replica3&collection=tm_order_ticket_01&shard=shard2"
curl"http://10.1.195.241:6804/solr/admin/cores?action=CREATE&name=tm_order_ticket_01_shard2_replica4&collection=tm_order_ticket_01&shard=shard2"
3、Solr配置表新增数据
比如新增租户21,那么cs_cfg_solr_21表中也需要配置类似其他租户的数据。
创建一个shard的脚本实例:
/home/aisolr/solrcloud/cloud-scripts/zkcli.sh-cmd upconfig -zkhost 10.11.20.106:2181,10.11.20.106:2182,10.11.20.106:2183-confdir /home/aisolr/solrcloud/config-files/kb_prompts -confname kb_prompts_01_conf
/home/aisolr/solrcloud/cloud-scripts/zkcli.sh-cmd linkconfig -collection kb_prompts_01 -confname kb_prompts_01_conf -zkhost10.11.20.106:2181,10.11.20.106:2182,10.11.20.106:2183
curl"http://10.11.20.106:6801/solr/admin/collections?action=CREATE&name=kb_prompts_01&numShards=1"
curl"http://10.11.20.106:6801/solr/admin/cores?action=CREATE&name=kb_prompts_01_shard1_replica2&collection=kb_prompts_01&shard=shard1"
curl"http://10.11.20.106:6802/solr/admin/cores?action=CREATE&name=kb_prompts_01_shard1_replica3&collection=kb_prompts_01&shard=shard1"
curl"http://10.11.20.106:6804/solr/admin/cores?action=CREATE&name=kb_prompts_01_shard1_replica4&collection=kb_prompts_01&shard=shard1"
7.11 在用zkCli.sh启动客户端时,Will not attempt to authenticate using SASL (无法定位登录配置)
这是由于没有启用用户验证导致的,不影响使用,但影响安全性。SASL 即:Simple Authentication and SecurityLayer
可能是hosts有问题,打开hosts文件,果然有xxxxxx.xxx.com域名的hosts,而zoo.cfg用的是ip,可能是这个问题。把zoo.cfg用域名映射,然后hosts,重启,好了。
7.12 Zookeeper的ip或port变更涉及到的调整?
需要对应调整AICS.CS_CFG_SOLR对应租户的表中的ZK_HOST_ADDRESS字段值。
7.13 如何删除索引数据?(一)
(1)删除一个colllection的所有数据。(最为常用,特别是线上前,我们要清理所有流水表数据一样)
说明:向一个core/replica发起请求,会将该core对应的所对应collection全部清除,无需向不同的replica上发送多次,他们内部有同步策略。
格式:curlhttp://localhost:8983/solr/{corename}/update --data-binary"<delete><query>*:*</query></delete>" -H"Content-type:text/xml;charset=UTF-8"
举例:curl http://10.11.20.107:6801/solr/sr_ticket_01_shard2_replica1/update--data-binary"<delete><query>*:*</query></delete>" -H"Content-type:text/xml;charset=UTF-8"
(2)删除指定id数据:
举例:curl http://localhost:8983/solr/{corename}/update--data-binary "<delete><id>1</id></delete>"-H "Content-type:text/xml;charset=UTF-8"
7.14 如何删除索引命令?(二)
删除solr索引数据,使用XML有两种写法:
1)
<delete><id>1</id></delete>
<commit/>
2)
<delete><query>id:1</query></delete>
<commit/>
删除所有索引,这样写就可以了:
<delete><query>*:*</query></delete>
<commit/>
注意:这个<commit/>节点不能少,否则删除动作的事务不会提交。
删除索引管理界面运行:
也可以使用POST数据到这个URL方式运行:
http://localhost:8899/solr/mycore/update?wt=json
POST数据为:
<add commitWithin="1000" overwrite="true">
<delete>
<query>id:1</query>
</delete>
<commit></commit>
</add>
7.15 如何原子更新文档指定字段值?
参见:
7.16 常用linux打包命令?
tar -zcvf solr.tar.gz solr,solr目录下面的所有文件都会打包到jar中,也可以打包多个文件:tar–zcvfsolr.tar.gz solr*,此时将打包以solr*开头的所有文件。
举例:
7.17 快速重新搭建环境步骤?
一、背景:
1、当一个环境无法使用,重启等各种方法都无法解决,需要重新搭建环境。
二、前提条件:
1、 Tomcat正常部署
2、 Zookeeper正常部署
3、 solrcloud配置完成
三、 快速重新搭建环境步骤
部署环境例如:
注意:下面的ip需要替换为你的环境的ip地址。
1、停掉tomcat(如果两台主机,需要都停掉)
shutdowntomcat.sh
2、检查是否停掉tomcat(如果两台主机,检查两台主机)
打开solr url是否正常展示
3、停掉zookeeper(如果两台主机,需要都停掉)
shutdownzookeeper.sh
4、检查是否停掉zookeeper(如果两台主机,都需要检查)
zookeeperstatus.sh
5、删除所有数据(如果两台主机,需要都删除)
-- 清理索引数据
rm -rf ~/solrcloud/solrhome*/sr_*
rm -rf ~/solrcloud/solrhome*/kb_*
rm -rf ~/solrcloud/solrhome*/tm_*
-- 清理zk数据
rm -rf ~/solrcloud/zookeeper*/data/version*
rm -rf ~/solrcloud/zookeeper*/log/*
6、启动zookeeper(如果两台主机,需要都启动)
startzookeeper.sh
7、检查zookeeper是否启动(如果两台主机,需要都检查)
zookeeperstatus.sh
8、检查tomcat是否启动(如果两台主机,需要都检查)
starttomcat.sh
9、上传配置文件(如果两台主机,在其中一台机器上执行即可)
举例:
########kb_doc_request_21########updatezookeeper config########
~/solrcloud/cloud-scripts/zkcli.sh -cmdupconfig -zkhost 10.226.2.81:48600,10.226.2.81:48605,10.226.2.82:48610 -confdir~/solrcloud/config-files/kb_doc_request -confname kb_doc_request_21_conf
~/solrcloud/cloud-scripts/zkcli.sh -cmdlinkconfig -collection kb_doc_request_21 -confname kb_doc_request_21_conf-zkhost 10.226.2.81:48600,10.226.2.81:48605,10.226.2.82:48610
10、检查配置文件是否上传成功(该步骤可选)
(1)登陆ZK主机
zookeeper-3.4.5-1/bin/zkCli.sh -server10.226.2.81:48600
(2)查看ZK文件
ls /
ls /configs
ls /collections
11、创建collection(在一个IP上执行即可)
举例:
########kb_doc_request_21########createcollection########
curl"http://10.226.2.81:48500/solr/admin/collections?action=CREATE&name=kb_doc_request_21&numShards=2&maxShardsPerNode=4&createNodeSet=10.226.2.81:48500_solr"
curl"http://10.226.2.81:48502/solr/admin/cores?action=CREATE&name=kb_doc_request_21_shard1_replica2&collection=kb_doc_request_21&shard=shard1"
curl "http://10.226.2.82:48504/solr/admin/cores?action=CREATE&name=kb_doc_request_21_shard1_replica3&collection=kb_doc_request_21&shard=shard1"
curl"http://10.226.2.82:48506/solr/admin/cores?action=CREATE&name=kb_doc_request_21_shard1_replica4&collection=kb_doc_request_21&shard=shard1"
curl "http://10.226.2.81:48502/solr/admin/cores?action=CREATE&name=kb_doc_request_21_shard2_replica2&collection=kb_doc_request_21&shard=shard2"
curl"http://10.226.2.82:48504/solr/admin/cores?action=CREATE&name=kb_doc_request_21_shard2_replica3&collection=kb_doc_request_21&shard=shard2"
7.18 异常
7.18.1 异常java.io.IOException:Packet len4883899 is out of range!
异常:
2014-08-14 22:15:42.360 1972487[main-SendThread(localhost:2182)] WARN org.apache.zookeeper.ClientCnxn –Session 0x247d4b9e8b60016 for server localhost/127.0.0.1:2182, unexpectederror, closing socket connection and attempting reconnect
java.io.IOException:Packet len4883899 is out of range!
atorg.apache.zookeeper.ClientCnxnSocket.readLength(ClientCnxnSocket.java:112)
at org.apache.zookeeper.ClientCnxnSocketNIO.doIO(ClientCnxnSocketNIO.java:79)
atorg.apache.zookeeper.ClientCnxnSocketNIO.doTransport(ClientCnxnSocketNIO.java:355)
atorg.apache.zookeeper.ClientCnxn$SendThread.run(ClientCnxn.java:1068)
2014-08-14 22:15:42.463 1972590[main-EventThread] INFO org.apache.solr.common.cloud.ConnectionManager –Watcher org.apache.solr.common.cloud.ConnectionManager@201548d3name:ZooKeeperConnection Watcher:localhost:2181,localhost:2182,localhost:2183got event WatchedEvent state:Disconnected type:None path:null path:nulltype:None
2014-08-14 22:15:42.464 1972591[main-EventThread] INFO org.apache.solr.common.cloud.ConnectionManager –zkClient has disconnected
2014-08-14 22:15:43.316 1973443[main-EventThread] INFO org.apache.solr.common.cloud.ConnectionManager –Watcher org.apache.solr.common.cloud.ConnectionManager@201548d3name:ZooKeeperConnection Watcher:localhost:2181,localhost:2182,localhost:2183got event WatchedEvent state:SyncConnected type:None path:null path:nulltype:None
2014-08-14 22:15:44.117 1974244[main-SendThread(localhost:2181)] WARN org.apache.zookeeper.ClientCnxn –Session 0x247d4b9e8b60016 for server localhost/127.0.0.1:2181, unexpectederror, closing socket connection and attempting reconnect
当出现该异常时,界面有时也会展示:
Loading of"/solr/zookeeper?wt=json" failed (HTTP-Status 500)
"org.apache.zookeeper.KeeperException$ConnectionLossException:KeeperErrorCode = ConnectionLoss for /overseer/queue"
解决办法:
the problem wasthat the znode in question has been overwhelmed with sub-znodes. It had about 5million of them. Zookeeper apparently does not like this. Even worse, there isno great way to clean it up. ZK should have a prune command (or something).Thanks for the answers.
if a znode is not readable, increasejute.maxbuffer
1、look for "Packet len <xx> is out of range" in theclient log
2、increase it by 20%
3、set in JVMFLAGS="-Djute.maxbuffer=yy" bin/zkCli.sh
4、fixed in ZOOKEEPER-1513
jute.maxbuffer为byte。配置实例:
JAVA_OPTS=" -Xms1024m -Xmx1024m-XX:PermSize=128M -XX:MaxPermSize=256m -Dappframe.client.app.name=solr1-Dfile.encoding=GBK -Djava.awt.headless=true -Dfile.encoding=gbk-Doracle.jdbc.V8Compatible=true-Dsolr.solr.home=/home/aisolr/solrcloud/solrhome1-DzkHost=localhost:2181,localhost:2182,localhost:2183 -Djetty.port=6801 -Djute.maxbuffer=6000000 "
7.18.2 然后solr数据、zk数据后,仍无法创建collection,报错。
异常:
[ERROR] [2014-07-21 16:39:55,906] [http-9090-1] [servlet.SolrDispatchFilter] - [null:org.apache.solr.common.SolrException
at org.apache.solr.handler.admin.CollectionsHandler.handleResponse(CollectionsHandler.java:248)
at org.apache.solr.handler.admin.CollectionsHandler.handleResponse(CollectionsHandler.java:233)
at org.apache.solr.handler.admin.CollectionsHandler.handleCreateAction(CollectionsHandler.java:368)
at org.apache.solr.handler.admin.CollectionsHandler.handleRequestBody(CollectionsHandler.java:141)
at org.apache.solr.handler.RequestHandlerBase.handleRequest(RequestHandlerBase.java:135)
at org.apache.solr.servlet.SolrDispatchFilter.handleAdminRequest(SolrDispatchFilter.java:720)
at org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:265)
at org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:205)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:857)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
at java.lang.Thread.run(Thread.java:662)
解决办法:
删除webapps中solr.war解压后的内容。
每次搭建新环境也不能带有solr文件夹。
posted on 2017-10-25 14:49 cxhfuujust 阅读(996) 评论(0) 编辑 收藏 举报