solr DIH 知识梳理

web.xml中listener配置

  1. <listener>
  2. <listener-class>org.apache.solr.handler.dataimport.scheduler.ApplicationListener</listener-class>
  3. </listener>

配置文件dataimport.properties

  1. #################################################
  2. # #
  3. # dataimport scheduler properties #
  4. # #
  5. #################################################
  6. # to sync or not to sync
  7. # 1 - active; anything else - inactive
  8. syncEnabled=1
  9. # which cores to schedule
  10. # in a multi-core environment you can decide which cores you want syncronized
  11. # leave empty or comment it out if using single-core deployment
  12. syncCores=core1,core2
  13. # solr server name or IP address
  14. # [defaults to localhost if empty]
  15. server=localhost
  16. # solr server port
  17. # [defaults to 80 if empty]
  18. port=8080
  19. # application name/context
  20. # [defaults to current ServletContextListener's context (app) name]
  21. webapp=solr
  22. # URL params [mandatory]
  23. # remainder of URL
  24. params=/dataimport?command=delta-import&clean=false&commit=true
  25. # schedule interval
  26. # number of minutes between two runs
  27. # [defaults to 30 if empty]
  28. interval=1
  29. # 重做索引的时间间隔,单位分钟,默认7200,即5天;
  30. # 为空,为0,或者注释掉:表示永不重做索引
  31. reBuildIndexInterval=7200
  32. # 重做索引的参数
  33. reBuildIndexParams=/dataimport?command=full-import&clean=true&commit=true
  34. # 重做索引时间间隔的计时开始时间,第一次真正执行的时间=reBuildIndexBeginTime+reBuildIndexInterval*60*1000;
  35. # 两种格式:2012-04-11 03:10:00 或者 03:10:00,后一种会自动补全日期部分为服务启动时的日期
  36. reBuildIndexBeginTime=03:10:00
  • interval增量索引的频率,每隔interval分钟就启动一次task
  1. timer.scheduleAtFixedRate(task, startTime, 60000 * interval);
  • 关于reBuildIndexBeginTime,这里表现为fullImportStartTime
  • 增量更新的请求参数params=/dataimport?command=delta-import&clean=false&commit=true
  1. fullImportTimer.scheduleAtFixedRate(fullImportTask, fullImportStartTime, 60000 * reBuildIndexInterval);

data-config.xml配置

  • query是获取全部数据的SQL(solr从sql中获取那些数据),多列

  • deltaImportQuery是获取增量数据时使用的SQL(数据库新增数据追加到solr的数据),多列

  • deltaQuery是获取pk的SQL(数据库新增数据是,追加到solr的数据时的条件,根据id

    ,条件是最后一次获取的时间,${dataimporter.last_index_time,最后获取的时间}),一列

  • 这个是在增量时使用的修改语句,其中需要注意的是dataimporter.delta这个前缀一定要带

    pk,根据我艰苦卓绝的跟踪代码知道这个pk其实作用只是用来对deltaQuery查询出来的内容放入到一个map中的时候作为key用的

    如果你不想deltaQuery查询出来的结果最后出现混乱,那么最好保证pk是唯一的

  1. document中使用的是自己要被加入到索引中的field
  2. query,被用来做为全量导入的时候使用
  3. deltaImportQuery 这个是在增量时使用的修改语句,其中需要注意的是dataimporter.delta这个前缀一定要带
  4. pk,根据我艰苦卓绝的跟踪代码知道这个pk其实作用只是用来对deltaQuery查询出来的内容放入到一个map中的时候作为key用的
  5. 如果你不想deltaQuery查询出来的结果最后出现混乱,那么最好保证pk是唯一的
  6. deltaQuery,这个是用来查询需要被更新的对象的主键,一边deltaImportQuery使用
  7. transformer:很多时候数据库中的字段不能满足你的需要,比如存储了用户生日,那么你需要将他的生肖存储,则此时需要对生日做自己的处理

增量索引

  1. 终止跑索引:http://localhost:8080/solr/collection1/dataimport?command=abort
  2. 开始索引:http://localhost:8080/solr/collection1/dataimport?command=full-import
  3. 增量索引 http://localhost:8080/solr/collection1/dataimport?command=delta-import

源代码apache-solr-dataimportscheduler-1.4.jar

ApplicationListener

  1. package org.apache.solr.handler.dataimport.scheduler;
  2. import java.util.Calendar;
  3. import java.util.Date;
  4. import java.util.Timer;
  5. import javax.servlet.ServletContext;
  6. import javax.servlet.ServletContextEvent;
  7. import javax.servlet.ServletContextListener;
  8. import org.slf4j.Logger;
  9. import org.slf4j.LoggerFactory;
  10. public class ApplicationListener
  11. implements ServletContextListener
  12. {
  13. private static final Logger logger = LoggerFactory.getLogger(ApplicationListener.class)
  14. ;
  15. public void contextDestroyed(ServletContextEvent servletContextEvent)
  16. {
  17. ServletContext servletContext = servletContextEvent.getServletContext();
  18. Timer timer = (Timer)servletContext.getAttribute("timer");
  19. Timer fullImportTimer = (Timer)servletContext
  20. .getAttribute("fullImportTimer");
  21. if (timer != null)
  22. timer.cancel();
  23. if (fullImportTimer != null) {
  24. fullImportTimer.cancel();
  25. }
  26. servletContext.removeAttribute("timer");
  27. servletContext.removeAttribute("fullImportTimer");
  28. }
  29. public void contextInitialized(ServletContextEvent servletContextEvent)
  30. {
  31. ServletContext servletContext = servletContextEvent.getServletContext();
  32. try
  33. {
  34. Timer timer = new Timer();
  35. //增量索引task
  36. DeltaImportHTTPPostScheduler task = new DeltaImportHTTPPostScheduler(servletContext
  37. .getServletContextName(), timer);
  38. //配置的间隔时间分钟
  39. int interval = task.getIntervalInt();
  40. Calendar calendar = Calendar.getInstance();
  41. calendar.add(12, interval);
  42. Date startTime = calendar.getTime();
  43. //task调度
  44. timer.scheduleAtFixedRate(task, startTime, 60000 * interval);
  45. servletContext.setAttribute("timer", timer);
  46. Timer fullImportTimer = new Timer();
  47. //全量索引task
  48. FullImportHTTPPostScheduler fullImportTask = new FullImportHTTPPostScheduler(servletContext
  49. .getServletContextName(), fullImportTimer);
  50. //这里重建索引时间
  51. int reBuildIndexInterval = fullImportTask
  52. .getReBuildIndexIntervalInt();
  53. if (reBuildIndexInterval <= 0) {
  54. logger.warn("Full Import Schedule disabled");
  55. return;
  56. }
  57. Calendar fullImportCalendar = Calendar.getInstance();
  58. Date beginDate = fullImportTask.getReBuildIndexBeginTime();
  59. fullImportCalendar.setTime(beginDate);
  60. fullImportCalendar.add(12, reBuildIndexInterval);
  61. Date fullImportStartTime = fullImportCalendar.getTime();
  62. //fullImportStartTime这个跟配置文件里的reBuildIndexBeginTime相关,
  63. fullImportTimer.scheduleAtFixedRate(fullImportTask, fullImportStartTime, 60000 * reBuildIndexInterval);
  64. servletContext.setAttribute("fullImportTimer", fullImportTimer);
  65. }
  66. catch (Exception e) {
  67. if (e.getMessage().endsWith("disabled"))
  68. logger.warn("Schedule disabled");
  69. else
  70. logger.error("Problem initializing the scheduled task: ", e);
  71. }
  72. }
  73. }

增量,全量索引task都继承自BaseTimerTask,主要都差不多,看BaseTimerTask的prepUrlSendHttpPost就好

增量索引task

  1. package org.apache.solr.handler.dataimport.scheduler;
  2. import java.util.Timer;
  3. import org.slf4j.Logger;
  4. import org.slf4j.LoggerFactory;
  5. public class DeltaImportHTTPPostScheduler extends BaseTimerTask
  6. {
  7. private static final Logger logger = LoggerFactory.getLogger(DeltaImportHTTPPostScheduler.class)
  8. ;
  9. public DeltaImportHTTPPostScheduler(String webAppName, Timer t) throws Exception
  10. {
  11. super(webAppName, t);
  12. logger.info("<index update process> DeltaImportHTTPPostScheduler init");
  13. }
  14. public void run()
  15. {
  16. try {
  17. if ((this.server.isEmpty()) || (this.webapp.isEmpty()) || (this.params == null) ||
  18. (this.params
  19. .isEmpty())) {
  20. logger.warn("<index update process> Insuficient info provided for data import");
  21. logger.info("<index update process> Reloading global dataimport.properties");
  22. reloadParams();
  23. }
  24. else if (this.singleCore) {
  25. prepUrlSendHttpPost(this.params);
  26. }
  27. else if ((this.syncCores.length == 0) || ((this.syncCores.length == 1) &&
  28. (this.syncCores[0]
  29. .isEmpty()))) {
  30. logger.warn("<index update process> No cores scheduled for data import");
  31. logger.info("<index update process> Reloading global dataimport.properties");
  32. reloadParams();
  33. }
  34. else {
  35. for (String core : this.syncCores)
  36. prepUrlSendHttpPost(core, this.params);
  37. }
  38. }
  39. catch (Exception e) {
  40. logger.error("Failed to prepare for sendHttpPost", e);
  41. reloadParams();
  42. }
  43. }
  44. }

全量索引task

  1. package org.apache.solr.handler.dataimport.scheduler;
  2. import java.util.Timer;
  3. import org.slf4j.Logger;
  4. import org.slf4j.LoggerFactory;
  5. public class FullImportHTTPPostScheduler extends BaseTimerTask
  6. {
  7. private static final Logger logger = LoggerFactory.getLogger(FullImportHTTPPostScheduler.class)
  8. ;
  9. public FullImportHTTPPostScheduler(String webAppName, Timer t) throws Exception
  10. {
  11. super(webAppName, t);
  12. logger.info("<index update process> DeltaImportHTTPPostScheduler init");
  13. }
  14. public void run()
  15. {
  16. try {
  17. if ((this.server.isEmpty()) || (this.webapp.isEmpty()) || (this.reBuildIndexParams == null) ||
  18. (this.reBuildIndexParams
  19. .isEmpty())) {
  20. logger.warn("<index update process> Insuficient info provided for data import, reBuildIndexParams is null");
  21. logger.info("<index update process> Reloading global dataimport.properties");
  22. reloadParams();
  23. }
  24. else if (this.singleCore) {
  25. prepUrlSendHttpPost(this.reBuildIndexParams);
  26. }
  27. else if ((this.syncCores.length == 0) || ((this.syncCores.length == 1) &&
  28. (this.syncCores[0]
  29. .isEmpty()))) {
  30. logger.warn("<index update process> No cores scheduled for data import");
  31. logger.info("<index update process> Reloading global dataimport.properties");
  32. reloadParams();
  33. }
  34. else {
  35. for (String core : this.syncCores)
  36. prepUrlSendHttpPost(core, this.reBuildIndexParams);
  37. }
  38. }
  39. catch (Exception e) {
  40. logger.error("Failed to prepare for sendHttpPost", e);
  41. reloadParams();
  42. }
  43. }
  44. }

BaseTimerTask的,这里主要关注prepUrlSendHttpPost

  1. package org.apache.solr.handler.dataimport.scheduler;
  2. import java.io.IOException;
  3. import java.net.HttpURLConnection;
  4. import java.net.MalformedURLException;
  5. import java.net.URL;
  6. import java.text.DateFormat;
  7. import java.text.ParseException;
  8. import java.text.SimpleDateFormat;
  9. import java.util.Date;
  10. import java.util.Timer;
  11. import java.util.TimerTask;
  12. import org.slf4j.Logger;
  13. import org.slf4j.LoggerFactory;
  14. public abstract class BaseTimerTask extends TimerTask
  15. {
  16. protected String syncEnabled;
  17. protected String[] syncCores;
  18. protected String server;
  19. protected String port;
  20. protected String webapp;
  21. protected String params;
  22. protected String interval;
  23. protected String cores;
  24. protected SolrDataImportProperties p;
  25. protected boolean singleCore;
  26. protected String reBuildIndexParams;
  27. protected String reBuildIndexBeginTime;
  28. protected String reBuildIndexInterval;
  29. private String webAppName;
  30. protected static final Logger logger = LoggerFactory.getLogger(BaseTimerTask.class)
  31. ;
  32. public BaseTimerTask(String webAppName, Timer t) throws Exception
  33. {
  34. this.webAppName = webAppName;
  35. this.p = new SolrDataImportProperties();
  36. reloadParams();
  37. if (!this.syncEnabled.equals("1")) {
  38. throw new Exception("Schedule disabled");
  39. }
  40. if ((this.syncCores == null) || ((this.syncCores.length == 1) &&
  41. (this.syncCores[0]
  42. .isEmpty()))) {
  43. this.singleCore = true;
  44. logger.info("<index update process> Single core identified in dataimport.properties");
  45. } else {
  46. this.singleCore = false;
  47. logger.info(new StringBuilder().append("<index update process> Multiple cores identified in dataimport.properties. Sync active for: ").append(this.cores).toString());
  48. }
  49. }
  50. protected void reloadParams()
  51. {
  52. this.p.loadProperties(true);
  53. this.syncEnabled = this.p.getProperty("syncEnabled");
  54. this.cores = this.p.getProperty("syncCores");
  55. this.server = this.p.getProperty("server");
  56. this.port = this.p.getProperty("port");
  57. this.webapp = this.p.getProperty("webapp");
  58. this.params = this.p.getProperty("params");
  59. this.interval = this.p.getProperty("interval");
  60. this.syncCores = (this.cores != null ? this.cores.split(",") : null);
  61. this.reBuildIndexParams = this.p
  62. .getProperty("reBuildIndexParams");
  63. this.reBuildIndexBeginTime = this.p
  64. .getProperty("reBuildIndexBeginTime");
  65. this.reBuildIndexInterval = this.p
  66. .getProperty("reBuildIndexInterval");
  67. fixParams(this.webAppName);
  68. }
  69. protected void fixParams(String webAppName) {
  70. if ((this.server == null) || (this.server.isEmpty()))
  71. this.server = "localhost";
  72. if ((this.port == null) || (this.port.isEmpty()))
  73. this.port = "8080";
  74. if ((this.webapp == null) || (this.webapp.isEmpty()))
  75. this.webapp = webAppName;
  76. if ((this.interval == null) || (this.interval.isEmpty()) || (getIntervalInt() <= 0))
  77. this.interval = "30";
  78. if ((this.reBuildIndexBeginTime == null) || (this.reBuildIndexBeginTime.isEmpty()))
  79. this.interval = "00:00:00";
  80. if ((this.reBuildIndexInterval == null) || (this.reBuildIndexInterval.isEmpty()) ||
  81. (getReBuildIndexIntervalInt() <= 0))
  82. this.reBuildIndexInterval = "0";
  83. }
  84. protected void prepUrlSendHttpPost(String params)
  85. {
  86. sendHttpPost(null, params);
  87. }
  88. protected void prepUrlSendHttpPost(String coreName, String params)
  89. {
  90. sendHttpPost(coreName, params);
  91. }
  92. protected void sendHttpPost(String coreName, String params)
  93. {
  94. DateFormat df = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss SSS");
  95. Date startTime = new Date();
  96. String core = coreName == null ? "" : new StringBuilder().append("[").append(coreName).append("] ").toString();
  97. logger.info(new StringBuilder().append(core).append("<index update process> Process started at .............. ")
  98. .append(df
  99. .format(startTime))
  100. .toString());
  101. try
  102. {
  103. String completeUrl = buildUrl(coreName, params);
  104. URL url = new URL(completeUrl);
  105. HttpURLConnection conn = (HttpURLConnection)url.openConnection();
  106. conn.setRequestMethod("POST");
  107. conn.setRequestProperty("type", "submit");
  108. conn.setDoOutput(true);
  109. conn.connect();
  110. logger.info(new StringBuilder().append(core).append("<index update process> Full URL\t\t\t\t").append(conn.getURL()).toString());
  111. logger.info(new StringBuilder().append(core).append("<index update process> Response message\t\t\t")
  112. .append(conn
  113. .getResponseMessage()).toString());
  114. logger.info(new StringBuilder().append(core).append("<index update process> Response code\t\t\t")
  115. .append(conn
  116. .getResponseCode()).toString());
  117. if (conn.getResponseCode() != 200) {
  118. reloadParams();
  119. }
  120. conn.disconnect();
  121. logger.info(new StringBuilder().append(core).append("<index update process> Disconnected from server\t\t").append(this.server).toString());
  122. Date endTime = new Date();
  123. logger.info(new StringBuilder().append(core).append("<index update process> Process ended at ................ ")
  124. .append(df
  125. .format(endTime))
  126. .toString());
  127. } catch (MalformedURLException mue) {
  128. logger.error("Failed to assemble URL for HTTP POST", mue);
  129. } catch (IOException ioe) {
  130. logger.error("Failed to connect to the specified URL while trying to send HTTP POST", ioe);
  131. }
  132. catch (Exception e)
  133. {
  134. logger.error("Failed to send HTTP POST", e);
  135. }
  136. }
  137. private String buildUrl(String coreName, String params) { StringBuilder sb = new StringBuilder();
  138. sb.append("http://").append(this.server).append(":").append(this.port);
  139. if (!this.webapp.startsWith("/"))
  140. sb.append("/");
  141. sb.append(this.webapp);
  142. if ((coreName != null) && (!coreName.isEmpty())) {
  143. if (!this.webapp.endsWith("/"))
  144. sb.append("/");
  145. sb.append(coreName);
  146. }
  147. if (sb.charAt(sb.length() - 1) == '/') {
  148. if (params.startsWith("/"))
  149. sb.setLength(sb.length() - 1);
  150. }
  151. else if (!params.startsWith("/")) {
  152. sb.append("/");
  153. }
  154. sb.append(params);
  155. return sb.toString(); }
  156. public int getIntervalInt() {
  157. try {
  158. return Integer.parseInt(this.interval);
  159. } catch (NumberFormatException e) {
  160. logger.warn("Unable to convert 'interval' to number. Using default value (30) instead", e);
  161. }
  162. return 30;
  163. }
  164. public int getReBuildIndexIntervalInt()
  165. {
  166. try {
  167. return Integer.parseInt(this.reBuildIndexInterval);
  168. } catch (NumberFormatException e) {
  169. logger.info("Unable to convert 'reBuildIndexInterval' to number. do't rebuild index.", e);
  170. }
  171. return 0;
  172. }
  173. public Date getReBuildIndexBeginTime()
  174. {
  175. Date beginDate = null;
  176. try {
  177. SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd");
  178. String dateStr = sdfDate.format(new Date());
  179. beginDate = sdfDate.parse(dateStr);
  180. if ((this.reBuildIndexBeginTime == null) ||
  181. (this.reBuildIndexBeginTime
  182. .isEmpty()))
  183. return beginDate;
  184. SimpleDateFormat sdf;
  185. if (this.reBuildIndexBeginTime.matches("\\d{2}:\\d{2}:\\d{2}")) {
  186. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  187. beginDate = sdf.parse(new StringBuilder().append(dateStr).append(" ").append(this.reBuildIndexBeginTime).toString());
  188. }
  189. else if (this.reBuildIndexBeginTime
  190. .matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}"))
  191. {
  192. sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  193. }
  194. return sdf.parse(this.reBuildIndexBeginTime);
  195. }
  196. catch (ParseException e)
  197. {
  198. logger.warn("Unable to convert 'reBuildIndexBeginTime' to date. use now time.", e);
  199. }
  200. return beginDate;
  201. }
  202. }

最后还有Properties文件相关的类SolrDataImportProperties

  1. package org.apache.solr.handler.dataimport.scheduler;
  2. import java.io.FileInputStream;
  3. import java.io.FileNotFoundException;
  4. import java.io.IOException;
  5. import java.util.Properties;
  6. import org.apache.solr.core.SolrResourceLoader;
  7. import org.slf4j.Logger;
  8. import org.slf4j.LoggerFactory;
  9. public class SolrDataImportProperties
  10. {
  11. private Properties properties;
  12. public static final String SYNC_ENABLED = "syncEnabled";
  13. public static final String SYNC_CORES = "syncCores";
  14. public static final String SERVER = "server";
  15. public static final String PORT = "port";
  16. public static final String WEBAPP = "webapp";
  17. public static final String PARAMS = "params";
  18. public static final String INTERVAL = "interval";
  19. public static final String REBUILDINDEXPARAMS = "reBuildIndexParams";
  20. public static final String REBUILDINDEXBEGINTIME = "reBuildIndexBeginTime";
  21. public static final String REBUILDINDEXINTERVAL = "reBuildIndexInterval";
  22. private static final Logger logger = LoggerFactory.getLogger(SolrDataImportProperties.class)
  23. ;
  24. public void loadProperties(boolean force)
  25. {
  26. try
  27. {
  28. SolrResourceLoader loader = new SolrResourceLoader(null);
  29. logger.info("Instance dir = " + loader.getInstanceDir());
  30. String configDir = loader.getConfigDir();
  31. configDir = SolrResourceLoader.normalizeDir(configDir);
  32. if ((force) || (this.properties == null)) {
  33. this.properties = new Properties();
  34. String dataImportPropertiesPath = configDir + "dataimport.properties";
  35. FileInputStream fis = new FileInputStream(dataImportPropertiesPath);
  36. this.properties.load(fis);
  37. }
  38. } catch (FileNotFoundException fnfe) {
  39. logger.error("Error locating DataImportScheduler dataimport.properties file", fnfe);
  40. }
  41. catch (IOException ioe)
  42. {
  43. logger.error("Error reading DataImportScheduler dataimport.properties file", ioe);
  44. }
  45. catch (Exception e)
  46. {
  47. logger.error("Error loading DataImportScheduler properties", e);
  48. }
  49. }
  50. public String getProperty(String key) {
  51. return this.properties.getProperty(key);
  52. }
  53. }

服务端流程主要流程

  1. handleRequestBody--> importer.runCmd-->doFullImport/doDeltaImport--> docBuilder.execute()--> doDelta()-->collectDelta()-->ject> row = epw.nextModifiedRowKey()/getModifiedParentRows-->entityProcessor.nextModifiedParentRowKey()-->initQuery()-->dataSource.getData(q)-->ResultSetIterator r = new ResultSetIterator(query)-->getARow()

剩下的就跟正常的solr处理流程差不多了

links

Solr中DIH模式的使用

solr连接数据库配置

Solr学习(五)DIH增量、定时导入并检索数据

solr DIH 知识梳理的更多相关文章

  1. [SQL] SQL 基础知识梳理(一)- 数据库与 SQL

    SQL 基础知识梳理(一)- 数据库与 SQL [博主]反骨仔 [原文地址]http://www.cnblogs.com/liqingwen/p/5902856.html 目录 What's 数据库 ...

  2. [SQL] SQL 基础知识梳理(二) - 查询基础

    SQL 基础知识梳理(二) - 查询基础 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5904824.html 序 这是<SQL 基础知识梳理( ...

  3. [SQL] SQL 基础知识梳理(三) - 聚合和排序

    SQL 基础知识梳理(三) - 聚合和排序 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5926689.html 序 这是<SQL 基础知识梳理 ...

  4. [SQL] SQL 基础知识梳理(四) - 数据更新

    SQL 基础知识梳理(四) - 数据更新 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5929786.html 序 这是<SQL 基础知识梳理( ...

  5. [SQL] SQL 基础知识梳理(五) - 复杂查询

    SQL 基础知识梳理(五) - 复杂查询 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5939796.html 序 这是<SQL 基础知识梳理( ...

  6. Solr DIH JDBC 源码解析

    Solr DIH 源码解析 DataImportHandler.handleRequestBody()中的importer.runCmd(requestParams, sw) if (DataImpo ...

  7. Anliven - 基础知识梳理汇总 - 软件测试

    基础知识梳理 - 软件测试 - 概念 基础知识梳理 - 软件测试 - 分类 基础知识梳理 - 软件测试 - 流程 基础知识梳理 - 软件测试 - 用例 基础知识梳理 - 软件测试 - 方法 基础知识梳 ...

  8. Solrj和Solr DIH索引效率对比分析

    测试软件环境: 1.16G windows7 x64  32core cpu . 2.jdk 1.7  tomcat 6.x  solr 4.8 数据库软件环境: 1.16G windows7 x64 ...

  9. [C# 基础知识梳理系列]专题六:泛型基础篇——为什么引入泛型

    引言: 前面专题主要介绍了C#1中的2个核心特性——委托和事件,然而在C# 2.0中又引入一个很重要的特性,它就是泛型,大家在平常的操作中肯定会经常碰到并使用它,如果你对于它的一些相关特性还不是很了解 ...

随机推荐

  1. php curl 多线程方法

    <?php /** * curl 多线程 * @param array $array 并行网址 * @param int $timeout 超时时间 * @return array */ fun ...

  2. JS打开新页面跳转

      有时候使用js进行页面跳转,想使用 a 标签中 target="_blank" 形式,跳转打开一个新的页面. 可以使用以下脚本,创建一个 a标签,然后模拟点击操作. 代码如下: ...

  3. sublime注释插件DocBlockr

    DocBlockr很好用,不仅仅可以自动生成注释,还可以手动编辑注释的格式. 安装方法:  Cmd+Shift+P -> Install Package -> docblockr  win ...

  4. 变量声明---let,const,解构

    let在很多方面与var是相似的,但是可以帮助大家避免在JavaScript里常见一些问题. const是对let的一个增强,它能阻止对一个变量再次赋值. 块作用域 当用let声明一个变量,它使用的是 ...

  5. css3中变形函数(同样是对元素来说的)和元素通过改变自身属性达到动画效果

    /*对元素进行改变(移动.变形.伸缩.扭曲)*/ .wrapper{ margin:100px 100px auto auto; width:300px; height:200px; border:2 ...

  6. 自定义TextView 调用ttf格式字体

    自定义TextView 调用ttf格式字体 1.<strong>将ttf格式文件存放在assets/fonts/下</strong> 注:PC系统字体存放在C:\Windows ...

  7. windows server 2008 wamp安装报000F15A0解决方法

    wampserver2.2c-x64 原因:缺少Visual C++ 2008 Runtime x64,官网地址http://www.microsoft.com/zh-cn/download/deta ...

  8. jquery动态添加的html,第三方插件无法生效的情况

    今天一个问题纠结了半天,问题如下图  问题大致就是如上,新增的内容死活点不起,插件没有生效,在一个装逼前端群里面问,给我的答案是叫我去了解事件委托,了解一下事件冒泡!! 好吧,我一上午加半个下午的时间 ...

  9. JHChart iOS图表工具库1.0.3新版本详解

    前言. 从2016年4月14日开始,本人着手开发了JHChart图表工具库.经过断断续续的开发,截止到现在,已经实现了折线图.柱状图.饼状图.环形图和表格样式的图表功能.为了方便使用,我已经将一个简单 ...

  10. MFC操作注册表

    1.创建和修改注册表 BOOL CTestToolCtr::GetHkey(CString strHkey, HKEY& hkey) { == strHkey.CompareNoCase(_T ...