问题背景
公司内部从2015年9月开始部署了Solr5.3 搜素引擎服务,到今年年初Solr的版本已经发展到了Solr6.x,无奈开源社区的发张的确很快,因为Solr6服务端整合了Facebook的prestodb数据库的Sql解析引擎( http://prestodb-china.com/),可以让Solr6服务端支持简单的sql语句查询,一时间搜索引擎可以支持Sql语句查询,虽然目前只能支持很简单的单表查询语句和集中功能简单的聚合查询,总之Solr在nosql DB方面走出了坚实的一步。更加方便的是Solrj代码包中提供了一个实现了jdbc接口的API包,这样使得一般的开发工程师可以轻松编写基于搜索引擎的查询操作。
这么好的功能我们当然是尝试用一下的,所以当时以很快的速度在老版本Solr集群之上部署了新Solr6的collection,不过没有过多久发现了一个严重的问题。问题是,当调用类似以下API会出现问题:
http://10.1.5.19:8080/solr/admin/collections?action=CREATE&name=collection1 |
当调用admin/collections的API 来向集群提交一个创建新Collection的请求的时候,操作往往会超时,报序列化异常相关的异常,无论通过这个api创建的collection是solr5还是solr6的都会有问题,不能能成功创建。
其他几个api也无一例外的有这样的问题。比如:
curl 'http://localhost:8080/solr/admin/collections?action=DELETE&name=search4totalpay' |
原因分析
找原因的时候,很容易联想到是不是因为在原本只有solr5节点的集群中添加了版本为solr6的节点呢?最后看了一堆代码之后发现的确是因为在老集群中。
先来看一幅执行“/admin/collections” api的集群调用流程图,如下:
集群创建collection流程说明:
- 对solr集群任何一个节点发起/admin/collections?action=CREATE 这样的API请求(对应处理类:org.apache.solr.handler.admin.CollectionsHandler),
- 节点收收到请求之后,向zookeeper中写一个临时节点,节点内容是这次执行任务的内容(格式为json),在OverseerCollectionProcessor中的run() 方法中执行,最终执行任务会代理到org.apache.solr.cloud.OverseerCollectionProcessor.Runner 类上执行。
- 上图中背景标注为黄色的节点(该节点为监工节点,是由所有节点竞争出来的)认领zookeeper临时节点的任务,
- 监工(overseer)节点执行该节点中的任务,当action=CREATE时,就是向其他节点再分发创建collection replic节点的命令了,对应该的命令路径为“/admin/cores”
- 等到所有replic都创建成功之后,有overseer节点再向zookeeper中写一个执行成功的标记,最终用户就能感知到本次任务已经执行成功了(当然该任务的执行也能够通过异步的方式来执行)
到此,问题的关键点就明白了,关键在第二和第三步,由solr6向solr5节点(高版本节点向低版本)发送/admin/collections 这类请求是存在问题,Solr API在向前兼容上存在问题,但是测试中发现由solr5向solr6发送请求是没有问题,也就是Solr向后兼容没有问题
解决之法:
了解了问题点,现在就能着手解决了。很简单,只需要在Solr6引擎节点启的时候,将Solr6节点参与Overseer角色竞争的流程去掉,让Solr6节点没有机会成为Overseer角色,要达到这个目的,先要了解一下Overseer竞选相关的代码结构。
先看以下类图:
ZkController初始化时,会初始化LeaderElector对象,LeaderElector会将本地节点信息预先写到/overseer_elect/election 节点下参与leader竞选。solr有两种场景需要竞选Leader,一种是上面说到的集群监工负责执行提交给cloud任务,另外一个是执行share内master任务的节点。
执行逻辑分别被封装在OverseerElectionContext 和ShardLeaderElectionContextBase中。
需要修改的点是,ZkController中init()方法中:
private void init(CurrentCoreDescriptorProvider registerOnReconnect) { try { createClusterZkNodes(zkClient); zkStateReader.createClusterStateWatchersAndUpdate(); // start the overseer first as following code may need it's processing if (!zkRunOnly) { //▼▼▼▼▼▼20161022 baisui add for solr6和solr5的节点在同一个集群中,solr的节点会抢overseer的角色但是执行不正常所以就把它抢overseer的 // 的功能去掉,这样overseer的角色只会有solr5的节点抢夺到 overseerElector = new LeaderElector(zkClient){ @Override public int joinElection(ElectionContext context, boolean replacement) throws KeeperException, InterruptedException, IOException { return 0; } @Override public int joinElection(ElectionContext context, boolean replacement, boolean joinAtHead) throws KeeperException, InterruptedException, IOException { return 0; } @Override void retryElection(ElectionContext context, boolean joinAtHead) throws KeeperException, InterruptedException, IOException { } }; //▲▲▲▲▲▲ this.overseer = new Overseer(cc.getShardHandlerFactory().getShardHandler(), cc.getUpdateShardHandler(), CommonParams.CORES_HANDLER_PATH, zkStateReader, this, cloudConfig); ElectionContext context = new OverseerElectionContext(zkClient, overseer, getNodeName()); overseerElector.setup(context); overseerElector.joinElection(context, false); } Stat stat = zkClient.exists(ZkStateReader.LIVE_NODES_ZKNODE, null, true); if (stat != null && stat.getNumChildren() > 0) { publishAndWaitForDownStates(); } // Do this last to signal we're up. createEphemeralLiveNode(); } catch (IOException e) { log.error("", e); throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Can't create ZooKeeperController", e); } catch (InterruptedException e) { // Restore the interrupted status Thread.currentThread().interrupt(); log.error("", e); throw new ZooKeeperException(SolrException.ErrorCode.SERVER_ERROR, "", e); } catch (KeeperException e) { log.error("", e); throw new ZooKeeperException(SolrException.ErrorCode.SERVER_ERROR, "", e); } }
如上只需要重载LeaderElector的三个方法,joinElection(),retryElection()在方法体中什么事儿都不干就能让Solr节点竞选集群监工的流程去掉了。
总结
至此,Solr5和Solr6 可以在一个Zookeeper域中共存,正常执行了。当然,最简单的办法是,另外启一个zk集群,把solr6的索引完全放到一个独立的集群中去,但是这样无形中增加了集群维护成本,得不偿失。
相关推荐
- **优势**:提高了Hadoop集群的利用率和灵活性,支持多种计算框架共存。 6. **Hive**: - **功能**:一种数据仓库工具,用于查询和管理Hadoop中的结构化数据。 - **特点**:提供SQL-like查询语言HQL,使得处理...
少儿编程scratch项目源代码文件案例素材-直升机飞行.zip
wanjunshe_Python-Tensorflow_12888_1745868924470
健康监测_Android开发_BLE蓝牙通信_心率数据采集与存储_基于小米手环2的实时心率监测应用_支持后台长时间运行的心率记录工具_可导出SQLite数据库的心率数据分析系统_适
少儿编程scratch项目源代码文件案例素材-种花模拟器.zip
嵌入式系统开发_FreeRTOS实时操作系统_STM32F103C8T6微控制器_OLED显示屏_DHT11温湿度传感器_多任务调度_多级菜单设计_万年历算法_电子闹钟功能_参数配
基于python实现的粒子群的VRP(车辆配送路径规划)问题建模求解+源码+项目文档+算法解析,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用,详情见md文档 算法设计的关键在于如何向表现较好的个体学习,标准粒子群算法引入惯性因子w、自我认知因子c1、社会认知因子c2分别作为自身、当代最优解和历史最优解的权重,指导粒子速度和位置的更新,这在求解函数极值问题时比较容易实现,而在VRP问题上,速度位置的更新则难以直接采用加权的方式进行,一个常见的方法是采用基于遗传算法交叉算子的混合型粒子群算法进行求解,这里采用顺序交叉算子,对惯性因子w、自我认知因子c1、社会认知因子c2则以w/(w+c1+c2),c1/(w+c1+c2),c2/(w+c1+c2)的概率接受粒子本身、当前最优解、全局最优解交叉的父代之一(即按概率选择其中一个作为父代,不加权)。 算法设计的关键在于如何向表现较好的个体学习,标准粒子群算法引入惯性因子w、自我认知因子c1、社会认知因子c2分别作为自身、当代最优解和历史最优解的权重,指导粒子速度和位置的更新,这在求解函数极值问题时比较容易实现,而在VRP问题上,速度位置的更新则难以直接采用加权的方式进行,一个常见的方法是采用基于遗传算法交叉算子的混合型粒子群算法进行求解,这里采用顺序交叉算子,对惯性因子w、自我认知因子c1、社会认知因子c2则以w/(w+c1+c2),c1/(w+c1+c2),c2/(w+c1+c2)的概率接受粒子本身、当前最优解、全局最优解交叉的父代之一(即按概率选择其中一个作为父代,不加权)。
scratch少儿编程逻辑思维游戏源码-猫猫粉碎.zip
scratch少儿编程逻辑思维游戏源码-蓝胡子.zip
scratch少儿编程逻辑思维游戏源码-美食大亨.zip
scratch少儿编程逻辑思维游戏源码-洛克人.zip
scratch少儿编程逻辑思维游戏源码-龙冲刺.zip
思幻个人引导页V2.2版本11月29日更新.zip
scratch少儿编程逻辑思维游戏源码-骑士风斩法.zip
移动应用开发_H5CSS3ionicng-cordovaMVVM模式_基于HTML5和CSS3技术实现多页面布局ionic指令数据绑定ui-route单页跳转调用手机
少儿编程scratch项目源代码文件案例素材-植物大战僵尸创造版 Ver. 1.0.3.zip
scratch少儿编程逻辑思维游戏源码-日落(2).zip
动态星空背景个人主页(带后台).zip
scratch少儿编程逻辑思维游戏源码-迷雾森林:诞生 3.2 起源觉醒.zip
lib文件