在中介绍了solrcloud的第一个模块---构建管理solr集群状态信息的zookeeper集群。当我们在solr服务器启动时拥有了这样一个zookeeper集群后,显然我们需要连接到zookeeper集群的方便手段,在这一篇中我将对zookeeper客户端相关的各个封装类进行分析。
solrzkclient类是solr服务器用来与zookeeper集群进行通信的接口类,它包含的主要组件有:
其中connectionmanager是watcher的实现类,主要负责对客户端与zookeeper集群之间连接的状态变化信息进行响应,关于watcher的详细介绍,可以参考,
solrzookeeper类是一个包装类,没有实际意义,zkcmdexecutor类是负责在连接失败的情况下,重试某种操作特定次数,具体的操作是zkoperation这个抽象类的具体实现子类,其execute方法中包含了具体操作步骤,这些操作包括新建一个znode节点,读取znode节点数据,创建znode路径,删除znode节点等zookeeper操作。
首先来看它的构造函数,先创建connectionmanager对象来响应两端之间的状态变化信息,然后zkclientconnectionstrategy类是一个连接策略抽象类,它包含连接和重连两种策略,并且采用模板方法模式,具体的实现是通过静态累不类zkupdate来实现的,defaultconnectionstrategy是它的一个实现子类,它覆写了connect和reconnect两个连接策略方法。
值得注意的是,构造函数中生成的zkupdate匿名类对象,它的update方法会被调用,
在这个方法里,会首先将已有的老的solrzookeeperg关闭掉,然后放置上一个新的solrzookeeper。做好这些准备工作以后,就会去连接zookeeper服务器集群,
connmanager.waitforconnected(clientconnecttimeout);//连接zk服务器集群,默认30秒超时时间
其实具体的连接动作是new solrzookeeper(serveraddress, timeout, watcher)引发的,上面那句代码只是在等待指定时间,看是否已经连接上。
如果连接zookeeper服务器集群成功,那么就可以进行zookeeper的常规操作了:
1) 是否已经连接
2) 是否存在某个路径的znode
3) 创建一个znode节点
4) 获取指定路径下的孩子znode节点
5) 获取指定znode上附加的数据
6) 在指定znode上设置数据
7) 创建路径
8) 删除指定znode
我们再回过头来看看connectionmanager类是如何响应两端的连接状态信息的变化的,它最重要的方法是process方法,当它被触发回调时,会从watchedevent参数中得到事件的各种状态信息,比如连接成功,会话过期(此时需要进行重连),连接断开等。
基本可以用上面这幅图来概述,这是一个拥有4个solr节点的集群,索引分布在两个shard里面,每个shard包含两个solr节点,一个是leader节点,一个是replica节点,此外集群中有一个负责维护集群状态信息的overseer节点,它是一个总控制器。集群的所有状态信息都放在zookeeper集群中统一维护。从图中还可以看到,任何一个节点都可以接收索引更新的请求,然后再将这个请求转发到文档所应该属于的那个shard的leader节点,leader节点更新结束完成,最后将版本号和文档转发给同属于一个shard的replicas节点。
下面我们来看一个简单的solrcloud集群的配置过程。
首先去的源码和二进制包,注意solr4.0现在还在开发中,因此这里是nightly build版本。
示例1,简单的包含2个shard的集群
这个示例中,我们把一个collection的索引数据分布到两个shard上去,步骤如下:
为了弄2个solr服务器,我们拷贝一份example目录
然后启动第一个solr服务器,并初始化一个新的solr集群,
-dzkrun参数是启动一个嵌入式的zookeeper服务器,它会作为solr服务器的一部分,-dbootstrap_confdir参数是上传本地的配置文件上传到zookeeper中去,作为整个集群共用的配置文件,-dnumshards指定了集群的逻辑分组数目。
然后启动第二个solr服务器,并将其引向集群所在位置
-dzkhost=localhost:9983就是指明了zookeeper集群所在位置
我们可以打开 或者看看目前集群的状态,
现在,我们可以试试索引一些文档,
最后,来试试分布式搜索吧:
zookeeper维护的集群状态数据是存放在solr/zoo_data目录下的。
现在我们来剖析下这样一个简单的集群构建的基本流程:
先从第一台solr服务器说起:
1) 它首先启动一个嵌入式的zookeeper服务器,作为集群状态信息的管理者,
2) 将自己这个节点注册到/node_states/目录下
3) 同时将自己注册到/live_nodes/目录下
4)创建/overseer_elect/leader,为后续overseer节点的选举做准备,新建一个overseer,
5) 更新/clusterstate.json目录下json格式的集群状态信息
6) 本机从zookeeper中更新集群状态信息,维持与zookeeper上的集群信息一致
7)上传本地配置文件到zookeeper中,供集群中其他solr节点使用
8) 启动本地的solr服务器,
9) solr启动完成后,overseer会得知shard中有第一个节点进来,更新shard状态信息,并将本机所在节点设置为shard1的leader节点,并向整个集群发布最新的集群状态信息。
10)本机从zookeeper中再次更新集群状态信息,第一台solr服务器启动完毕。
然后来看第二台solr服务器的启动过程:
1) 本机连接到集群所在的zookeeper,
2) 将自己这个节点注册到/node_states/目录下
3) 同时将自己注册到/live_nodes/目录下
4) 本机从zookeeper中更新集群状态信息,维持与zookeeper上的集群信息一致
5) 从集群中保存的配置文件加载solr所需要的配置信息
6) 启动本地solr服务器,
7) solr启动完成后,将本节点注册为集群中的shard,并将本机设置为shard2的leader节点,
8) 本机从zookeeper中再次更新集群状态信息,第二台solr服务器启动完毕。
示例2,包含2个shard的集群,每个shard中有replica节点
如图所示,集群包含2个shard,每个shard中有两个solr节点,一个是leader,一个是replica节点,
我们可以打开 看看包含4个节点的集群的状态,
这个集群现在就具备容错性了,你可以试着干掉一个solr服务器,然后再发送查询请求。背后的实质是集群的ov erseer会监测各个shard的leader节点,如果leader节点挂了,则会启动自动的容错机制,会从同一个shard中的其他replica节点集中重新选举出一个leader节点,甚至如果overseer节点自己也挂了,同样会自动在其他节点上启用新的overseer节点,这样就确保了集群的高可用性。
示例3 包含2个shard的集群,带shard备份和zookeeper集群机制
上一个示例中存在的问题是:尽管solr服务器可以容忍挂掉,但集群中只有一个zookeeper服务器来维护集群的状态信息,单点的存在即是不稳定的根源。如果这个zookeeper服务器挂了,那么分布式查询还是可以工作的,因为每个solr服务器都会在内存中维护最近一次由zookeeper维护的集群状态信息,但新的节点无法加入集群,集群的状态变化也不可知了。因此,为了解决这个问题,需要对zookeeper服务器也设置一个集群,让其也具备高可用性和容错性。
有两种方式可选,一种是提供一个外部独立的zookeeper集群,另一种是每个solr服务器都启动一个内嵌的zookeeper服务器,再将这些zookeeper服务器组成一个集群。 我们这里用后一种做示例:
我们可以打开 看看包含4个节点的集群的状态,可以发现其实和上一个没有任何区别。
后续的文章将从实现层面对solrcloud这个分布式搜索凯发天生赢家一触即发官网的解决方案进行进一步的深入剖析。
solrcloud最重要的一点就是引入了zookeeper来统一管理各种配置和状态信息。是一个开源分布式的服务,它提供了分布式协作,分布式同步,配置管理等功能. 其实现的功能与google的基本一致.zookeeper的官方网站已经写了一篇非常经典的概述性文章,请大家参阅:.
的示例中是在启动每个solr服务器前,内嵌启动了一个zookeeper服务器,再将这几台zookeeper服务器组成一个集群,确保solr集群信息的高可用性和容错性。
构建一个可用的zookeeper集群,这就是solrcloud要做的第一件工作。下面来看下solrcloud是如何实现这一功能的:
1) 首先在web.xml中配置了一个filter
在web容器启动时会去加载并初始化solrdispatchfilter这个filter,它的init方法会被调用,这个方法中做的最主要的事情是初始化一个solr核容器。
2) 初始化solr核容器时,首先找到solr的根目录,这个目录下最重要的是solr.xml这个配置文件,这个配置文件用于初始化容器中加载的各个solr核,如果没有提供solr.xml,则会启用默认的配置信息:
3) 初始化过程的其中一步就是初始化zookeeper服务器,你可以选择单机的zookeeper服务器,也可以构建zookeeper集群,下面以集群为例进行代码分析。
solrzkserver类就是伴随solr启动的内嵌的zookeeper服务器,首先来看parseconfig方法,它负责解析zoo.cfg文件,读取zookeeper启动时所需要的配置信息,这些配置信息由solrzkserverprops类表示,
首先设置zookeeper存储数据的目录
然后读取zoo.cfg配置文件中的信息,为启动zookeeper服务器提供完整的配置信息,
下面是一个示例配置文件:
注意,server.x这些行就指明了zookeeper集群所包含的机器名称,每台zookeeper服务器会使用3个端口来进行工作,其中第一个端口(端口1)用来做运行期间server间的通信,第二个端口(端口2)用来做leader election,另外还有一个端口(端口0)负责接收客户端请求。那么一台机器怎样确定自己是谁呢?这是通过datadir目录下的myid文本文件确定。myid文件只包含一个数字,内容就是所在server的id:quorumpeerconfig.myid。
1) 准备好集群所需要的配置信息后,就可以启动zookeeper集群了。启动时是生成一个zookeeper服务器线程,根据配置信息来决定是单机还是集群模式,如果是单机模式,则生成zookeeperservermain对象并启动,如果是集群模式,则使用quorumpeermain对象启动。最后将服务器线程设置为daemon模式,就完成了zookeeper服务器的启动工作了。
为了验证集群是否启动成功,可以使用zookeeper提供的命令行工具进行验证,进入bin目录下,运行:
这是连接到集群中1台zookeeper服务器,然后创建一个znode,往其中加入一些数据,你再连接到集群中其他的服务器上,查看数据是否一致,即可知道zookeeper集群是否已经构建成功。