solr入门之多线程操作solr中索引字段的解决
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; import javax.annotation.PostConstruct;
import javax.annotation.Resource; import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.common.SolrInputDocument;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import cn.com.mx.gome.search.core.util.PreIndexThreadPool;
import cn.com.mx.gome.search.core.util.prop.PropUtils;
import cn.com.mx.gome.suggest.cache.WordCountCache;
import cn.com.mx.gome.suggest.component.FullIndexProcessContainer;
import cn.com.mx.gome.suggest.component.FullIndexSuggestProcess;
import cn.com.mx.gome.suggest.constant.Const;
import cn.com.mx.gome.suggest.domian.BaseSuggestIndex;
import cn.com.mx.gome.suggest.service.FullIndexSuggestService;
import cn.com.mx.gome.suggest.solr.SolrServiceWrapper;
/**
* 搜索建议 索引库导入业务层
* @author songqinghu
*
*/
@Service("fullIndexSuggestServiceImpl")
public class FullIndexSuggestServiceImpl extends FullIndexServiceImpl implements FullIndexSuggestService { private Logger logger = LoggerFactory.getLogger(FullIndexSuggestServiceImpl.class); @Resource //容器类
private FullIndexProcessContainer fullIndexProcessContainer; @Value("${maxImumPoolSize}")//最大线程数目---通用能够提取到父类中
private int maxImumPoolSize; private int skip=0;//開始的角标 private int limit=1000;//默认的步距 private int rows=0;//建立索引的总数目 private long maxNum =0; //可回收线程池
private ExecutorService threadPool = Executors.newCachedThreadPool(); //solr连接
@Resource
private SolrServiceWrapper solrServiceWrapperImpl; private SolrClient suggestClient; @PostConstruct
private void getClient(){
suggestClient = solrServiceWrapperImpl.getCollection(getCollectionName());;
} @Override
public String getCollectionName() { return "meixin_suggest";
}
/**
* 搜索建议全量索引建立 详细实现方法
* 这种方法我想这样设计:
* 能够动态的加入和删除导入的过程类进入 多线程导入
* 做一个 集合类容器来完毕类的组装操作 方法通过接口的方式来进行统一的 处理过程控制关键点
*
* 1.先做一个圈子的索引建议词导入--仅仅导入圈子的名称
* ---从数据源中获取到所有的圈子名称--对圈子名称进行处理拼音加入---组装成bean 提交到solr中--> ok 最粗糙版本号 兴许能够优化下
* 2.商品搜索推荐词 导入---分析讨论下 (临时不在--无非三个来源 数据库 已有索引库 还有人工导入干预也是进数据库 这里要不要再设置一个插拔式的数据源接口呢?
* 先做个正规途径的数据获取流程
*/
@Override
public int index(SolrClient client ,boolean morethread) {
logger.warn("{} 開始全量导入",new Date());
try {
//每次进入全量导入方法 对计数器清零处理
skip = 0;
rows = 0;
limit = PropUtils.getInstance().getInt(Const.INDEX_FULL_LIMIT_GROUP, 1000);
logger.warn("时间{},初始化变量: skip:{},rows:{},limit{} ",new Date(),skip,rows,limit);
WordCountCache.clear();//清理本地词语次数缓存
//容器中获取种类
List<FullIndexSuggestProcess> processContainer = fullIndexProcessContainer.getProcessContainer();
for (FullIndexSuggestProcess fullprocess : processContainer) {
logger.warn("时间{},导入{}索引 ",new Date(),fullprocess.getClass().getName());
//多线程索引建立过程
//1.获取此时最大索引数目--通用方法
maxNum = fullprocess.getMaxNum();
//2.组建任务线程队列
ArrayList<Future<Integer>> futureTasks = new ArrayList<Future<Integer>>();
//3.向任务队列中加入任务到线程或者索引数终止
for (int i = 0; i < maxImumPoolSize; i++) { try {
if(!fullprocess.isEnd(skip,limit,maxNum)){//考虑兼容各种数据库问题---mongo 当相等时 尽管开启了线程可是取不到数据--能够考虑容错
break;
}else{
futureTasks.add(getFuture(client,fullprocess));
}
} catch (Exception e) {
logger.error("FullIndexSuggestServiceImpl addfuture : ",e);
}
} //4.循环任务队列 持续加入任务到索引导入结束
while(futureTasks.size()>0){
ArrayList<Future<Integer>> tmpFutureTasks = new ArrayList<Future<Integer>>();
for (Future<Integer> future : futureTasks) {
if(!future.isDone()){//该线程未运行结束 --加入到任务队列中继续运行
tmpFutureTasks.add(future);
}else{
//rows +=future.get();//统计索引数量--不是必需统计 数目是错的 中间可能会覆盖
if(fullprocess.isEnd(skip,limit,maxNum)){
tmpFutureTasks.add(getFuture(client,fullprocess));
}
}
}
futureTasks = tmpFutureTasks; Thread.sleep(500);
}
//一个全量导入结束 }
//所有的全量导入结束--这里开启异步线程池对索引词语出现的次数进行重写
threadPool.execute(new Runnable() {
@Override
public void run() {
List<List<SolrInputDocument>> list = WordCountCache.getDocs();
logger.warn("时间{},開始改动词频 ,循环次数{}",new Date(),list.size());
try {
for (List<SolrInputDocument> docs : list) {
suggestClient.add(docs);
}
} catch (SolrServerException |IOException e) {
logger.error(" index threadPool.execute : " + e);
}
}
}); } catch (Exception e) {
logger.error("FullIndexSuggestServiceImpl ERROR:", e);
}
return WordCountCache.getRows();
}
/**
*
* @描写叙述:通用任务组装类
* @param client solr客户端
* @param fullprocess 数据获取
* @return
* @return Future<Integer>
* @exception
* @createTime:2016年3月24日
* @author: songqinghu
*/
private Future<Integer> getFuture(SolrClient client, FullIndexSuggestProcess fullprocess) {
//任务类
IndexTask indexTask = new IndexTask();
//设置參数
indexTask.setParameters(skip, limit, client, fullprocess);
//提交开启任务---线程池的书写
Future<Integer> future = PreIndexThreadPool.getPool().submit(indexTask);
//变更控制变量--这里也要做成通用型的--都设置到各子的方法中?? skip = skip + limit;
//返回任务封装
return future;
} private static class IndexTask implements Callable<Integer>{ private int skip; //開始 坐标 private int limit; //步距 private SolrClient client; //客户端 private FullIndexSuggestProcess fullprocess; //未知数据源
/**
*
* @描写叙述:设置类中用到的參数
* @return void
* @exception
* @createTime:2016年3月24日
* @author: songqinghu
*/
public void setParameters(int skip,int limit,SolrClient client,FullIndexSuggestProcess fullprocess){
this.skip = skip;
this.limit = limit;
this.client = client;
this.fullprocess = fullprocess;
} @Override
public Integer call() throws Exception { //1 获取原始资源数据 对资源数据进行处理----->拼音处理--工具类-->组装返回的beans类---->组装类
List<BaseSuggestIndex> beans = fullprocess.getBeans(skip, limit);
//2 推断返回的创建成功的索引数目
if(beans !=null && beans.size()>0){
//3 //提交
client.addBeans(beans);
return beans.size();
}else{
return 0;
} } } }
索引导入类
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set; import javax.annotation.Resource;
import javax.management.RuntimeErrorException; import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import cn.com.mx.gome.search.core.util.MD5Utils;
import cn.com.mx.gome.search.core.util.Pinyin4jUtil;
import cn.com.mx.gome.search.core.util.SetToStringArrUtils;
import cn.com.mx.gome.search.quote.digger.solrbean.ProductBean;
import cn.com.mx.gome.suggest.cache.WordCountCache;
import cn.com.mx.gome.suggest.domian.BaseSuggestIndex;
import cn.com.mx.gome.suggest.solr.SolrServiceWrapper; /**
* 商品类 搜索建议推荐词导入
* @author songqinghu
*
*/
@Component("fullIndexItemSuggestProcess")
public class FullIndexItemSuggestProcess implements FullIndexSuggestProcess { private Logger logger = LoggerFactory.getLogger(FullIndexItemSuggestProcess.class); //这里注入一个工厂来生产指定的数据源获取类来获取不同的数据源类--先放着 //solr连接
@Resource
private SolrServiceWrapper solrServiceWrapperImpl; @Value("${SORL_PRODUCT_NAME}") //从 properties 文件里注入 solr连接的名称
private String solrProductName; @Value("${SORL_SUGGEST_NAME}")
private String solrSuggestName; private SolrClient itemClient; private SolrClient suggestClient; @Override
public List<BaseSuggestIndex> getBeans(int skip, int limit) {//这里在传一个 參数用来确定数据导入方式
Map<String, Integer> words ;
if(false){
//words = getDataBySQL(skip, limit); //从原始数据库中获取
}else {
words= getDataBySolr(skip, limit);//从solr中获取
} List<BaseSuggestIndex> assembleBeans = AssembleBeans(words); return assembleBeans;
}
/**
*
* @描写叙述:从solr索引库中获取数据
* @param skip 開始
* @param limit 步距
* @return
* @return List<Product>
* @exception
* @createTime:2016年3月25日
* @author: songqinghu
*/
private Map<String,Integer> getDataBySolr(int skip, int limit) {
//查询指定范围内的数据
SolrQuery query = new SolrQuery();
query.set("q", "*:*");
query.setStart(skip);
query.setRows(limit);
QueryResponse response;
Map<String,Integer> words = null;
try {
response = itemClient.query(query); logger.info("skip="+skip+",limit="+limit+",获取到索引数 : "+response.getResults().getNumFound()); List<ProductBean> productBeans = response.getBeans(ProductBean.class);
//数据处理部分---我要将获取到的数据中的 须要的三个字段的汉字 然后进行记录出现次数和去反复
words = suggestCollect(productBeans);
}catch (SolrServerException | IOException e) {
logger.error("FullIndexItemSuggestProcess getDataBySolr :" + e);
}
return words;
}
//将获取到的数据中的 须要的三个字段的汉字 然后进行记录出现次数和去反复
private Map<String, Integer> suggestCollect(List<ProductBean> productBeans) { Map<String, Integer> words = new HashMap<String,Integer>(); for (ProductBean productBean : productBeans) { //这里须要考虑词语切分问题---不太会处理 String name = productBean.getName(); //商品名称
mapOperation(words, name);
List<String> cateNames = productBean.getCateName();//类目名称
for (String cateName : cateNames) {
mapOperation(words, cateName);
}
String spuBrand = productBean.getSpuBrand();//品牌名称
mapOperation(words, spuBrand); }
return words;
}
/**
* 对map中数据进行操作
*/
private void mapOperation(Map<String, Integer> words,String name){
if(words.containsKey(name)){ //存在就加一
Integer count = words.get(name);
words.put(name, count+1);
}else{//不存在就存入
words.put(name, 1);
}
} //对原始查询后的集合数据进行拆分
public List<BaseSuggestIndex> AssembleBeans(Map<String,Integer> words){
ArrayList<BaseSuggestIndex> baseSuggestIndexs = new ArrayList<BaseSuggestIndex>();
Set<Entry<String, Integer>> entrySet = words.entrySet();
for (Entry<String, Integer> entry : entrySet) {
BaseSuggestIndex assembleBean = AssembleBean(entry);
baseSuggestIndexs.add(assembleBean);
} return baseSuggestIndexs;
}
//组装单个 solr文档对象
public BaseSuggestIndex AssembleBean(Entry<String, Integer> entry){ BaseSuggestIndex baseIndex = new BaseSuggestIndex(); String word = entry.getKey(); Set<String> shortpy = Pinyin4jUtil.converterToFirstSpellToSet(word); Set<String> allpy = Pinyin4jUtil.converterToSpellToSet(word); baseIndex.setWord(word); baseIndex.setShort_py(SetToStringArrUtils.convertToStringArr(shortpy)); baseIndex.setAll_py(SetToStringArrUtils.convertToStringArr(allpy));
//这里须要设置一下 使用md5加密算法 来保证 每一个字符串相应的id唯一 涉及到分类问题 可能会出现反复加入分类
String id = getId(word);
baseIndex.setSuggestId(id); baseIndex.setType("product"); baseIndex.setCreateTime(new Date().getTime());
//这里须要特别注意了--涉及到多线程问题了 ---当查询已经存在的词语的时候 查到次数加上当前的次数---
//存在问题--怎样处理线程顺序保证多个线程是有序的操作呢?---存入映射关系 结束后再次进行次数更新?
//这里必须要处理下 和实际的数据差距太大了了!!!!!!!!!!!!!
logger.info("词名: "+word + " 本轮次数 :"+ entry.getValue());
baseIndex.setCount(getCount4Word(id, entry.getValue())); return baseIndex;
}
/**
*
* @描写叙述:获取该词语的数量加上眼下的词语数量---首次从索引库中获取 后缓存到本地中 以后从本地中获取
* 这里还是不太准确 尽管保证了词语出现次数 通过加锁 可是线程提交时是无法进行控制的 还是存在误差的
* @param id
* @param count 本轮词语出现的次数
* @return
* @return Integer
* @exception
* @createTime:2016年3月25日
* @author: songqinghu
*/
private Integer getCount4Word(String id,Integer count ){ return WordCountCache.putCount(id, count);
} /**
*
* @描写叙述:通过数据库来获取数据源
* @param skip
* @param limit
* @return
* @return List<Product>
* @exception
* @createTime:2016年3月25日
* @author: songqinghu
*/
private Map<String, Integer> getDataBySQL(int skip, int limit) { return null;
} @Override
public long getMaxNum() {//这里也须要依据数据源来配置下
if(itemClient == null){
itemClient = solrServiceWrapperImpl.getCollection(solrProductName);
}
SolrQuery query = new SolrQuery();
query.set("q", "*:*");
long maxNum = 0;
try {
maxNum = itemClient.query(query).getResults().getNumFound();
} catch (SolrServerException | IOException e) {
logger.error("FullIndexItemSuggestProcess getMaxNum :" + e);
}
logger.info(new Date()+" 最大值: "+maxNum);
return maxNum;
} @Override
public boolean isEnd(int skip, int limit, long maxNum) { //開始坐标要是小于 最大数量就继续---要不要事实更新呢?时时更新吧 return skip < getMaxNum() ? true : false;
}
/**
* 获取词语相应的索引id
*/
@Override
public String getId(String word) {
if(word ==null){
throw new RuntimeException("id不能为空");
}
return "product_"+MD5Utils.MD5(word);
} }
本地容器类
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* 容器类 --对solr中涉及的词语出现的词语进行本地缓存 和 同步处理
* 分布式时能够考虑 放入 共享容器中
* @author songqinghu
*
*/
import java.util.concurrent.ConcurrentHashMap; import org.apache.solr.common.SolrInputDocument;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import cn.com.mx.gome.suggest.domian.BaseSuggestIndex; public class WordCountCache { private static Logger logger = LoggerFactory.getLogger(WordCountCache.class);
/**
* 容器 放入word的md5加密后的 和次数
*/
private static ConcurrentHashMap<String, Integer> wordCountMap = new ConcurrentHashMap<String,Integer>(); private static Object lock = new Object(); /**
*
* @描写叙述:设置词语的次数 ---设置锁处理
* @param word 词语(加密后的)id
* @param count 次数
* @return void
* @exception
* @createTime:2016年3月28日
* @author: songqinghu
*/
public static Integer putCount(String id,Integer count){
logger.warn("WordCountCache 线程: "+ Thread.currentThread().getName() +" id: "+id+" count: "+count);
synchronized (lock) {
Integer result = wordCountMap.get(id);
if(result !=null){ //存在此key 加上已经缓存的---不存在 直接存这次的
count = result + count;
}
wordCountMap.put(id, count);
return wordCountMap.get(id);
}
}
/**
*
* @描写叙述:是否缓存到了本地中
* @param id
* @return
* @return boolean
* @exception
* @createTime:2016年3月28日
* @author: songqinghu
*/
public static boolean containsKey(String id){
if(wordCountMap.containsKey(id)){
return true;
}
return false;
}
/**
*
* @描写叙述:清空本地的 缓存---全量结束 和 清理全量索引的时候
* @return
* @return boolean
* @exception
* @createTime:2016年3月28日
* @author: songqinghu
*/
public static boolean clear(){
wordCountMap.clear();
return true;
} /**
*
* @描写叙述:当全量索引导入结束后 获取缓存的词频 进行词频重写--后清空
* 这里还要优化下 当索引量非常多 不能一次所有都提交了--设置为加入到定量的list中
* @return
* @return List<SolrInputDocument>
* @exception
* @createTime:2016年3月28日
* @author: songqinghu
*/
public static List<List<SolrInputDocument>> getDocs(){
//文档容器
List<List<SolrInputDocument>> list = new ArrayList<List<SolrInputDocument>>(); ArrayList<SolrInputDocument> docs = new ArrayList<SolrInputDocument>();//測试一下 Set<Entry<String, Integer>> entrySet = wordCountMap.entrySet();
int i =0;
for (Entry<String, Integer> entry : entrySet) {
String id = entry.getKey();
Integer count = entry.getValue();
SolrInputDocument doc = new SolrInputDocument();
doc.setField(BaseSuggestIndex.Fd.suggestId.name(),id);//这里的硬编码能够设置成类的形式
Map<String, Integer> counts = new HashMap<String, Integer>(1);
counts.put("set", count);
doc.setField(BaseSuggestIndex.Fd.count.name(), counts);
docs.add(doc);
i++;
if(i>=1000){//为1000 单次改动文档数目设置为1000--总量最后一次进不来 的处理
ArrayList<SolrInputDocument> tmp = new ArrayList<SolrInputDocument>();
tmp.addAll(docs);
list.add(tmp);
i=0;
docs.clear();
}
}
if(i!=0){//最后一次没有进入推断里
list.add(docs);
}
return list;
}
/**
*
* @描写叙述:获取总数量--id就为总的索引数
* @return
* @return Integer
* @exception
* @createTime:2016年3月28日
* @author: songqinghu
*/
public static Integer getRows(){
return wordCountMap.size();
} }
solr入门之多线程操作solr中索引字段的解决的更多相关文章
- ORM中聚合函数、分组查询、Django开启事务、ORM中常用字段及参数、数据库查询优化
聚合函数 名称 作用 Max() 最大值 Min() 最小值 Sum() 求和 Count() 计数 Avg() 平均值 关键字: aggregate 聚合查询通常都是配合分组一起使用的 关于数据库的 ...
- Solr 16 - 增删改Solr中索引数据的几种方式 (在URL上或Web页面中操作)
目录 1 添加/更新索引数据 1.1 JSON格式的操作 1.2 XML格式的操作 2 删除索引数据 2.1 删除符合特定条件的数据 2.2 删除指定ID的数据 2.3 删除全部索引数据 3 在doc ...
- 使用solrj操作solr索引库
(solrj)初次使用solr的开发人员总是很郁闷,不知道如何去操作solr索引库,以为只能用<五分钟solr4.5教程(搭建.运行)>中讲到的用xml文件的形式提交数据到索引库,其实没有 ...
- 使用solrj操作solr索引库,solr是lucene服务器
客户端开发 Solrj 客户端开发 Solrj Solr是搭建好的lucene服务器 当然不可能完全满足一般的业务需求 可能 要针对各种的架构和业务调整 这里就需要用到Solrj了 Solrj是Sol ...
- Solr 13 - 在URL地址栏中操作Solr集群 - 包括CRUD、别名、切割分片、更新配置
目录 1 创建操作 1.1 创建collection 1.2 创建core 1.3 创建操作中的参数 2 删除操作 3 加载操作 4 查看操作 5 操作集合别名(操作成功, 但未查出区别) 6 切割分 ...
- python 操作solr索引数据
测试代码1: def test(self): data = {", "*字段名*": u"我是一个大好人"}}} params = {"bo ...
- 【solr】SolrCloud中索引数据存储于HDFS
SolrCloud中索引数据存储于HDFS 本人最近使用SolrCloud存储索引日志条件,便于快速索引,因为我的索引条件较多,每天日志记录较大,索引想到将日志存入到HDFS中,下面就说说怎么讲sol ...
- Solr入门之(6)配置文件solrconfig.xml
solrconfig.xml包含了用于配置自身行为的绝大部分参数,其作用范围是当前core.该文件位于${solr_home}/solr/core1/conf/下. 参数列表概览: A.lib B.d ...
- Solr入门之SolrServer实例化方式
随着solr版本的不断升级, 差异越来越大, 从以前的 solr1.2 到现在的 solr4.3, 无论是类还是功能都有很大的变换, 为了能及时跟上新版本的步伐, 在此将新版本的使用做一个简单的入门说 ...
随机推荐
- 第二天,学习if,变量,注释
zz_age = 31guss_age=int(input("input your answer:"))if guss_age == zz_age: print ("Yo ...
- SpringMVC中controller的跳转
controller中的重定向 (1)不需要传递参数重定向 方式一:使用ModelAndView return new ModelAndView("redirect:/toLi ...
- hadoop格式化出错,提示IO异常
配置好hadoop之后,在进行格式化的时候出现异常,原因是由于在core-site.xml 配置文件中写的路径格式不对. 不需要加 file:/ 或者 file:// 直接写绝对路径就行. <c ...
- HDU 3397 双lazy标记的问题
题目大意 对一个只有0和1的序列,支持以下几种操作1.将区间所有的值变成12.将区间所有的值变为03.将区间的0和1翻转(0变成1 1变成0)4.求区间中1的个数5.求区间连续最长的1的个数 http ...
- [转]maven编译时出现读取XXX时出错invalid LOC header (bad signature)
maven编译时出现读取XXX时出错invalid LOC header (bad signature) 一.发现问题右击pom.xml,run as —> maven install,会看到c ...
- poj1273最大流初破
第一次网络流,学了一天的DINIC算法(个人比较愚),切了这个入门题,开始的时候怎么调连 测试都过不了,后来发现犯了一个低级错误!把判断条件放在for(:)!里面和放在for下面大大 不同啊!里面的话 ...
- iOS 混合变换旋转 CGAffineTransform 的使用
在ios 中, Core Graphics 提供了一系列的函数可以在一个变换的基础上做深层次的变换,如果做一个既要缩放又要旋转的变换,以下的方法比较实用. CGAffineTransformScale ...
- 利用开源工具实现轻量级上网行为审计(来源ispublic.com)
https://blog.csdn.net/cnbird2008/article/details/5875781
- FF,chrome与IE的事件处理程序
今天学习了js的事件处理程序,IE与FF,chrome,safari,opera的处理事件方法不同,FF,chrome,safari,opera支持addEventLisener,而addEventL ...
- java.lang.ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet
新建Maven 项目的时候报错: java.lang.ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet ...