上一篇介绍了SolrCloud的基本概念,从这一篇开始我将深入到其实现代码中进行剖析。
- <filter>
- <filter-name>SolrRequestFilter</filter-name>
- <filter-class>org.apache.solr.servlet.SolrDispatchFilter</filter-class>
- </filter>
在web容器启动时会去加载并初始化SolrDispatchFilter这个filter,它的init方法会被调用,这个方法中做的最主要的事情是初始化一个Solr核容器。
- CoreContainer.Initializer init = createInitializer();
- // web.xml configuration
- this.pathPrefix = config.getInitParameter( "path-prefix" );
- is.cores = init.initialize();
2) 初始化Solr核容器时,首先找到solr的根目录,这个目录下最重要的是solr.xml这个配置文件,这个配置文件用于初始化容器中加载的各个solr核,如果没有提供solr.xml,则会启用默认的配置信息:
- private static final String DEF_SOLR_XML ="<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" +
- "<solr persistent=\"false\">\n" +
- " <cores adminPath=\"/admin/cores\" defaultCoreName=\"" + DEFAULT_DEFAULT_CORE_NAME + "\">\n" +
- " <core name=\""+ DEFAULT_DEFAULT_CORE_NAME + "\" shard=\"${shard:}\" instanceDir=\".\" />\n" +
- " </cores>\n" +
- "</solr>";
3) 初始化过程的其中一步就是初始化Zookeeper服务器,你可以选择单机的Zookeeper服务器,也可以构建Zookeeper集群,下面以集群为例进行代码分析。
- if (zkRun != null) {
- zkServer = new SolrZkServer(zkRun, zookeeperHost, solrHome, hostPort);
- zkServer.parseConfig();
- zkServer.start();
- // set client from server config if not already set
- if (zookeeperHost == null) {
- zookeeperHost = zkServer.getClientString();
- }
- }
SolrZkServer类就是伴随solr启动的内嵌的Zookeeper服务器,首先来看parseConfig方法,它负责解析zoo.cfg文件,读取Zookeeper启动时所需要的配置信息,这些配置信息由SolrZkServerProps类表示,
首先设置Zookeeper存储数据的目录
- if (zkProps == null) {
- zkProps = new SolrZkServerProps();
- // set default data dir
- // TODO: use something based on IP+port??? support ensemble all from same solr home?
- zkProps.setDataDir(solrHome + '/' + "zoo_data");
- zkProps.zkRun = zkRun;
- zkProps.solrPort = solrPort;
然后读取zoo.cfg配置文件中的信息,为启动zookeeper服务器提供完整的配置信息,
- props = SolrZkServerProps.getProperties(solrHome + '/' + "zoo.cfg");
- SolrZkServerProps.injectServers(props, zkRun, zkHost);
- zkProps.parseProperties(props);
下面是一个示例配置文件:
- tickTime=2000
- dataDir=/var/zookeeper/
- clientPort=2181
- initLimit=5
- syncLimit=2
- server.1=zoo1:2888:3888
- server.2=zoo2:2888:3888
- server.3=zoo3:2888:3888
注意,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服务器的启动工作了。
- public void start() {
- zkThread = new Thread() {
- @Override
- public void run() {
- try {
- if (zkProps.getServers().size() > 1) {//zk集群
- QuorumPeerMain zkServer = new QuorumPeerMain();
- zkServer.runFromConfig(zkProps);
- if (logger.isInfoEnabled()) {
- logger.info("启动zk服务器集群成功");
- }
- } else {//单机zk
- ServerConfig sc = new ServerConfig();
- sc.readFrom(zkProps);
- ZooKeeperServerMain zkServer = new ZooKeeperServerMain();
- zkServer.runFromConfig(sc);
- if (logger.isInfoEnabled()) {
- logger.info("启动单机zk服务器成功");
- }
- }
- logger.info("ZooKeeper Server exited.");
- } catch (Throwable e) {
- logger.error("ZooKeeper Server ERROR", e);
- throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
- }
- }
- };
- if (zkProps.getServers().size() > 1) {
- logger.info("STARTING EMBEDDED ENSEMBLE ZOOKEEPER SERVER at port " + zkProps.getClientPortAddress().getPort());
- } else {
- logger.info("STARTING EMBEDDED STANDALONE ZOOKEEPER SERVER at port " + zkProps.getClientPortAddress().getPort());
- }
- zkThread.setDaemon(true);
- zkThread.start();
- try {
- Thread.sleep(500); // pause for ZooKeeper to start
- } catch (Exception e) {
- logger.error("STARTING ZOOKEEPER", e);
- }
- }
为了验证集群是否启动成功,可以使用Zookeeper提供的命令行工具进行验证,进入bin目录下,运行:
- zkCli.cmd –server zookeeper服务器地址1:端口
这是连接到集群中1台Zookeeper服务器,然后创建一个ZNode,往其中加入一些数据,你再连接到集群中其他的服务器上,查看数据是否一致,即可知道Zookeeper集群是否已经构建成功。