solr学习笔记(二):进阶

这次讲讲solr4.0最新的云,其中solrj那段引用了其他人的结论,以后可能还会调整一下内容。

到1月23号已经更新了4.1,solrcloud改变不少,尤其是修复了一些bug, 4月最新版是4.3,也是增加了很多内容

1. solrcloud

    jetty启动: wiki有教程;
        实验遇到的问题:1. 在windows上不同jetty带起的solr实例注册不到zk上。
                                    2. 在虚拟机启动了2个shard,windows上的实例无法同步到已经启动的shard上,曾以为是solr目录文件不一样的问题,替换虚拟机中的目录到windows 下依然不行。
    tomcat+独立zookeeper: 
        1. 复制多份solr,和多份tomcat;配置单个solr运行在tomcat的实例(conf/Catalina/localhost/solr.xml); 注意如果在一台机器上tomcat服务器各种端口要避免重合!红字solr表示url的path。
        2. 将collection的配置上传到zk上: 利用 cloud-scripts目录下的zkcli.sh脚本
         cloud-scripts/zkcli.sh -cmd upconfig -confdir ~/apache-solr-4.0.0/example/solr/collection1/conf/ -confname coll1 -z vm:2181
        其中 -confdir 是上传集合的配置, -z 配置ZK的服务地址,coll1会保存在/configs下
        3. 在catalina.sh中加入JAVA_OPTS参数,JAVA_OPTS="-DzkHost=vm:2181" , 这是配置zk路径的。 如果第一台solr服务开始指定shard数量,还可以加入"-DnumShards=2",后续加入的机器可以不加这句
        4. 启动tomcat 和solr项目,在http://url/solr/#/~cloud中可以看到集群情况 
        5. 经验总结:
            5.1 如果不走第二步,那么需要把配置上传的语句(wiki中jetty启动有),写到tomcat的JAVA_OPTS里
            5.2 solr目录下的solr.xml中几个参数含义(只是注册到ZK上的属性,不影响使用,但点击cloud上的机器你就转不到那了):
                host:是注册到zookeeper集群的机器名.默认是机器名,可以改成机器的IP,那样就免去了更改系统hosts文件了.
                hostPort:注册到zookeeper集群的访问该服务的端口号.(最好改成所在tomcat服务端口)
                hostContext:注册到zookeeper集群的访问该服务器的web实例名.默认是solr
            5.3 如果首台solr实例启动不注明shard数量,那么集群中就1个shard后续所有实例都会注册到这个上,形成replica。即,numShards:默认为1,(shard才是真正分布式搜索的服务
            5.4 zk会保留集群注册信息,如果zk数据不删除,solr再次启动,还会处在相同配置上。
            5.5 如果开始只有一个shard,想热部署一个新的shard, 可以再solr.xml的 core上配置一个shard="shard2" 属性,启动之后自动加入集群之中。(这一步可以由5.6代替)
            5.6 复制一份collection1 到collection2, 在一台solr实例中启动,之后集群可见,其他节点可以再管理后台add core,(云端的add core 比单点的要多两个参数:collection和shard, 并且collection的schema和config都是zk上的路径,因此第二步上传配置是必须的。体现了集群管理配置的特点,对应solr中ZkResourceLoader干的事)形成新的分布式搜索库。可以为同一个collection增加不同shard
            5.7 solrcloud 4.0 master slave转换并没有想象中可靠,一般刚上线的master/slave都是正常状态,master挂机或者unload,slave多半变成等待恢复的状态而不是转为新master(master不在将不能进行写操作); 再恢复原master,slave多半出现恢复失败,但是当了新master。此时系统可用,任意一台机器down都不会影响读写(就算刚才新的master也挂了,原master也能顶上当master),务必进行重建等必要措施。
            5.8 5.7的问题在solrcloud4.1得到了解决,随时停掉master  都能自动改变leader,

之前的还是不要了,新探索出4.3的部署,之前的路线更简单一点: 
1. 解压solr-4.3.0, 把example拷贝出到新目录solrbase, 运行java -jar start.jar 把jetty自带的solr.war解压出来(不运行这一步解压不出solr所需要的所有lib,你以后要直接用cloud-script就不行)
2. 解压tomcat, 放到solrbase下,在tomcat/conf添加Catalina/localhost/solr.xml ,xml里添加如下内容:
<Context docBase="/xxx/tomcat/webapps/solr.war" debug="0" crossContext="true" >
           <Environment name="solr/home" type="java.lang.String" value="/xxx/solrbase/solr" override="true" />
        </Context>
3.将solrbase/webapps/solr.war拷贝到tomcat/webapps 下,tomcat的server.xml配置可以根据需要修改,尤其注意URIEncoding 
4. 拷贝solrbase/lib/* solrbase/resources/* 到tomcat/lib下(4.3才新增的要求)
5. 修改tomcat/bin/catalina.sh 添加类似JAVA_OPTS="-Dhost=vm -Dport=8080 -Dbootstrap_confdir=/data/solrbase/solr/collection1/conf -Dcollection.configName=collection1 -DzkHost=vm:2181/solr"  
!注意几个地方-Dhost 表示传入JVM的系统参数host, 这个就是填充solrbase/solr/solr.xml中的几个参数, 你可以先看看solrbase/solr/solr.xml <cores上面的几个参数,然后通过JAVA_OPTS的-D来传进来;指定zookeeper地址之后的/solr表示 solrcloud所需要的zk配置会放在/solr下,如果不指定,ZK根下会有一堆SOLR的各种配置;-Dbootstrap_confdir和 -Dcollection.configName=collection1 表示启动会把前者的配置上传到.../configs下,并且改名collection1,这是默认collection1例子所需要的配置,如果你不配置在这里,手动传上去也可以。
6. 打包solrbase 分发到其他机器上,启动tomcat/bin/startup.sh
7. 建议在solrcloud上的建立索引操作用collection API而不是每个solr实例慢慢add core 

    如果在schema.xml 定义中不存在text field ,在启动solr 时会出现下面的异常:org.apache.solr.common.SolrException: undefined field text。解决办法:只要在schema.xml 文件中增加一个text field ,这样在启动时就没有异常了。

如下:

<field name="text" type="text" stored="false" indexed="false"/>



2. <分词插件>

    analyzer是tokenizer和filter的组合: 覆盖public TokenStreamComponents createComponents(String fieldName, Reader reader) 方法,首先通过各种Tokenizer从reader中获取tokenizer,给TokenFilter产生各种TokenStream result,再进行过滤.. 最后返回new TokenStreamComponents(tokenizer, result)

    TokenizerFactory 需要重写Tokenizer create(Reader input)方法,smartcn直接返回了SentenceTokenizer

    TokenFilterFactory  需要重写 TokenFilter create(TokenStream input)  smartcn直接返回了WordTokenFilter(input)

    上述两个方法可以给solr在schema.xml上配置分词的组合细节的,如果直接用analyzer,那就不用再xml中配置其他过滤器之类的。

    可能一般需要自己的TokenFilter(smartcn的WordTokenFilter比较好理解); 4.0和3.0比在接口上有些许不同 需要特别留意。

    IK分词器中并没有TokenFilter 概念,而是在Tokenizer 中直接完成分词过程(注意Tokenizer 和TokenFilter 都需要覆盖的是boolean incrementToken()方法,并在类里面保留当前token信息)

    solr的分词基本与lucene的分词结构一致,多了直接暴露分词器内核的Factory

            
    分词组建实现了ResourceLoaderAware的XXXFactory的路径由SolrResourceLoader控制,如果想动态加载词典,就在inform(ResourceLoader loader)中启动监控线程即可。

3. solrj开发
    String url = "http://localhost:8080/solr/collection1"; //需要指定collection, 不指定就会使用solr.xml中的默认collection
    HttpSolrServer server = new HttpSolrServer(url);
    可以Pojo形式建文档
    HttpSolrServer 核心包装了一个httpClient,会对请求结果根据responseWriter进行再次解析.

     请求方面: 

    Without Solrj :如果采用http直接访问的方法,我们必然会用到httpclient请求Solr服务器。其中所有的搜索条件都必须通过拼接一个负责冗长的url,例如:q=tags:t5 AND t7&fl=auction_id&start=0&rows=4&sort=auction_id desc&…&...&… ,通过GET的方式,请求服务器。

    With Solrj :面对对象的思想,所有搜索条件均以setter属性的方式设置到其封装的对象当中。但是,实际上还是通过拼接url的方式,走http请求的方式再请求Solr服务器。

Result: 本质完全相同,有了Solrj,开发会省很多事,将很多开发中的体力活交给Solrj。但是直接拼接url的方式肯定比对象的方式灵活很多。


     获取结果方面:solrj比传统方式有着一些优势,使用默认的solrj 采用的 

    Without Solrj 

结果的返回形式是字符串,根据wt有XML和JSON两种格式

    With Solrj :解析solr返回的结果,支持两种方式,一种是字节流,直接得到序列化的对象,这是默认solrj采用的:wt=javabin。另一种字符流,目前只支持用XMLresponseparser帮你解析成对象,截止到4.1,JSON解析还没有。


4. http命令
    大量Http命令
    创建:http://vm:8082/solr/admin/cores?action=CREATE&name=coll1&instanceDir=coll1 &config=solrconfig.xml&schema=schema.xml&dataDir=data&shard=shard1&persist=true

5. 索引切换
    solr core提供swap的功能,单机上能立刻有效果,4.0云上不同replica切换之后不能完全起效果。(据说目前不支持)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
说明:依赖jar包:lucene-core-2.3.2.jar、IKAnalyzer3.2.8.jar。 一、LuceneUtil 工具类代码: package com.zcm.lucene; import java.io.File; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.List; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.Term; import org.apache.lucene.queryParser.MultiFieldQueryParser; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.Hits; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.wltea.analyzer.IKSegmentation; import org.wltea.analyzer.Lexeme; /** * Apache Lucene全文检索和IKAnalyzer分词工具类 * Company: 91注册码 * time:2014-04-22 * @author www.91zcm.com * @date * @version 1.1 */ public class LuceneUtil { /**索引创建的路径**/ private static String LucenePath = "d://index"; /** * 创建索引 * @throws Exception */ public static int createIndex(List list) throws Exception{ /**这里放索引文件的位置**/ File indexDir = new File(LucenePath); Analyzer luceneAnalyzer = new StandardAnalyzer(); /**注意最后一个boolean类型的参数:表示是否重新创建,true表示新创建(以前存在时回覆盖)**/ IndexWriter indexWriter = new IndexWriter(indexDir, luceneAnalyzer,true); for (int i = 0; i < list.size(); i++) { LuceneVO vo = (LuceneVO)list.get(i); Document doc = new Document(); Field FieldId = new Field("aid", String.valueOf(vo.getAid()),Field.Store.YES, Field.Index.NO); Field FieldTitle = new Field("title", vo.getTitle(), Field.Store.YES,Field.Index.TOKENIZED,Field.TermVector.WITH_POSITIONS_OFFSETS); Field FieldRemark = new Field("remark", vo.getRemark(), Field.Store.YES,Field.Index.TOKENIZED,Field.TermVector.WITH_POSITIONS_OFFSETS); doc.add(FieldId); doc.add(FieldTitle); doc.add(FieldRemark); indexWriter.addDocument(doc); } /**查看IndexWriter里面有多少个索引**/ int num = indexWriter.docCount(); System.out.println("总共------》" + num); indexWriter.optimize(); indexWriter.close(); return num; } /** * IKAnalyzer分词 * @param word * @return * @throws IOException */ public static List tokenWord(String word) throws IOException{ List tokenArr = new ArrayList(); StringReader reader = new StringReader(word); /**当为true时,分词器进行最大词长切分**/ IKSegmentation ik = new IKSegmentation(reader, true); Lexeme lexeme = null; while ((lexeme = ik.next()) != null){ tokenArr.add(lexeme.getLexemeText()); } return tokenArr; } /** * 创建索引(单个) * @param list * @throws Exception */ public static void addIndex(LuceneVO vo) throws Exception { /**这里放索引文件的位置**/ File indexDir = new File(LucenePath); Analyzer luceneAnalyzer = new StandardAnalyzer(); IndexWriter indexWriter = new IndexWriter(indexDir, luceneAnalyzer, false); /**增加document到索引去 **/ Document doc = new Document(); Field FieldId = new Field("aid", String.valueOf(vo.getAid()),Field.Store.YES, Field.Index.NO); Field FieldTitle = new Field("title", vo.getTitle(), Field.Store.YES,Field.Index.TOKENIZED,Field.TermVector.WITH_POSITIONS_OFFSETS); Field FieldRemark = new Field("remark", vo.getRemark(), Field.Store.YES,Field.Index.TOKENIZED,Field.TermVector.WITH_POSITIONS_OFFSETS); doc.add(FieldId); doc.add(FieldTitle); doc.add(FieldRemark); indexWriter.addDocument(doc); /**optimize()方法是对索引进行优化 **/ indexWriter.optimize(); indexWriter.close(); } /** * 创建索引(多个) * @param list * @throws Exception */ public static void addIndexs(List list) throws Exception { /**这里放索引文件的位置**/ File indexDir = new File(LucenePath); Analyzer luceneAnalyzer = new StandardAnalyzer(); IndexWriter indexWriter = new IndexWriter(indexDir, luceneAnalyzer,false); /**增加document到索引去 **/ for (int i=0; i<list.size();i++){ LuceneVO vo = (LuceneVO)list.get(i); Document doc = new Document(); Field FieldId = new Field("aid", String.valueOf(vo.getAid()),Field.Store.YES, Field.Index.NO); Field FieldTitle = new Field("title", vo.getTitle(), Field.Store.YES,Field.Index.TOKENIZED,Field.TermVector.WITH_POSITIONS_OFFSETS); Field FieldRemark = new Field("remark", vo.getRemark(), Field.Store.YES,Field.Index.TOKENIZED,Field.TermVector.WITH_POSITIONS_OFFSETS); doc.add(FieldId); doc.add(FieldTitle); doc.add(FieldRemark); indexWriter.addDocument(doc); } /**optimize()方法是对索引进行优化 **/ indexWriter.optimize(); indexWriter.close(); } /** * 更新索引(单个) * @param list * @throws Exception */ public static void updateIndex(LuceneVO vo) throws Exception { /**这里放索引文件的位置**/ File indexDir = new File(LucenePath); Analyzer luceneAnalyzer = new StandardAnalyzer(); IndexWriter indexWriter = new IndexWriter(indexDir, luceneAnalyzer,false); /**增加document到索引去 **/ Document doc = new Document(); Field FieldId = new Field("aid", String.valueOf(vo.getAid()),Field.Store.YES, Field.Index.NO); Field FieldTitle = new Field("title", vo.getTitle(), Field.Store.YES,Field.Index.TOKENIZED,Field.TermVector.WITH_POSITIONS_OFFSETS); Field FieldRemark = new Field("remark", vo.getRemark(), Field.Store.YES,Field.Index.TOKENIZED,Field.TermVector.WITH_POSITIONS_OFFSETS); doc.add(FieldId); doc.add(FieldTitle); doc.add(FieldRemark); Term term = new Term("aid",String.valueOf(vo.getAid())); indexWriter.updateDocument(term, doc); /**optimize()方法是对索引进行优化 **/ indexWriter.optimize(); indexWriter.close(); } /** * 创建索引(多个) * @param list * @throws Exception */ public static void updateIndexs(List list) throws Exception { /**这里放索引文件的位置**/ File indexDir = new File(LucenePath); Analyzer luceneAnalyzer = new StandardAnalyzer(); IndexWriter indexWriter = new IndexWriter(indexDir, luceneAnalyzer,false); /**增加document到索引去 **/ for (int i=0; i<list.size();i++){ LuceneVO vo = (LuceneVO)list.get(i); Document doc = new Document(); Field FieldId = new Field("aid", String.valueOf(vo.getAid()),Field.Store.YES, Field.Index.NO); Field FieldTitle = new Field("title", vo.getTitle(), Field.Store.YES,Field.Index.TOKENIZED,Field.TermVector.WITH_POSITIONS_OFFSETS); Field FieldRemark = new Field("remark", vo.getRemark(), Field.Store.YES,Field.Index.TOKENIZED,Field.TermVector.WITH_POSITIONS_OFFSETS); doc.add(FieldId); doc.add(FieldTitle); doc.add(FieldRemark); Term term = new Term("aid",String.valueOf(vo.getAid())); indexWriter.updateDocument(term, doc); } /**optimize()方法是对索引进行优化 **/ indexWriter.optimize(); indexWriter.close(); } /** * 创建索引(单个) * @param list * @throws Exception */ public static void deleteIndex(LuceneVO vo) throws Exception { /**这里放索引文件的位置**/ File indexDir = new File(LucenePath); Analyzer luceneAnalyzer = new StandardAnalyzer(); IndexWriter indexWriter = new IndexWriter(indexDir, luceneAnalyzer,false); Term term = new Term("aid",String.valueOf(vo.getAid())); indexWriter.deleteDocuments(term); /**optimize()方法是对索引进行优化 **/ indexWriter.optimize(); indexWriter.close(); } /** * 创建索引(多个) * @param list * @throws Exception */ public static void deleteIndexs(List list) throws Exception { /**这里放索引文件的位置**/ File indexDir = new File(LucenePath); Analyzer luceneAnalyzer = new StandardAnalyzer(); IndexWriter indexWriter = new IndexWriter(indexDir, luceneAnalyzer,false); /**删除索引 **/ for (int i=0; i<list.size();i++){ LuceneVO vo = (LuceneVO)list.get(i); Term term = new Term("aid",String.valueOf(vo.getAid())); indexWriter.deleteDocuments(term); } /**optimize()方法是对索引进行优化 **/ indexWriter.optimize(); indexWriter.close(); } /** * 检索数据 * @param word * @return */ public static List search(String word) { List list = new ArrayList(); Hits hits = null; try { IndexSearcher searcher = new IndexSearcher(LucenePath); String[] queries = {word,word}; String[] fields = {"title", "remark"}; BooleanClause.Occur[] flags = {BooleanClause.Occur.SHOULD, BooleanClause.Occur.SHOULD}; Query query = MultiFieldQueryParser.parse(queries, fields, flags, new StandardAnalyzer()); if (searcher != null) { /**hits结果**/ hits = searcher.search(query); LuceneVO vo = null; for (int i = 0; i < hits.length(); i++) { Document doc = hits.doc(i); vo = new LuceneVO(); vo.setAid(Integer.parseInt(doc.get("aid"))); vo.setRemark(doc.get("remark")); vo.setTitle(doc.get("title")); list.add(vo); } } } catch (Exception ex) { ex.printStackTrace(); } return list; } } Lucene用到的JavaBean代码: package com.zcm.lucene; /** * Apache Lucene全文检索用到的Bean * Company: 91注册码 * time:2014-04-22 * @author www.91zcm.com * @date * @version 1.1 */ public class LuceneVO { private Integer aid; /**文章ID**/ private String title; /**文章标题**/ private String remark; /**文章摘要**/ public Integer getAid() { return aid; } public void setAid(Integer aid) { this.aid = aid; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark; } } 备注:源码来源于www.91zcm.com 开源博客中的全文检索代码。(http://www.91zcm.com/)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值