使用 Solr 脚本 ‘bin/solr’ 启动服务

Solr 包含一个名为“bin/solr”的脚本,允许您在 Solr 安装或集群上执行许多常见操作。

关于 Solr 控制脚本的指导手册:https://solr.apache.org/guide/7_7/solr-control-script-reference.html

以 SolrCloud 模式启动 Solr 服务:bin/solr start -cloud

上面的命令是在 SolrCloud 模式下启动 Solr,它还会将启动 Solr 附带的嵌入式 ZooKeeper 实例(该选项可以简化为 -c 既: bin/solr start -c)。

如果我们已经有可用的 ZooKeeper 服务来替代 Solr 提供的嵌入式(单节点)ZooKeeper,可以在 solr.in.sh /solr.in.cmd 中指定 ZK_HOST,它会覆盖 bin/solr 使用的默认值,这样我们就不需要使用 -z 参数来指定 ZooKeeper 服务地址。

  • Linux: solr.in.sh
    # Set the ZooKeeper connection string if using an external ZooKeeper ensemble
    # e.g. host1:2181,host2:2181/chroot
    # Leave empty if not using SolrCloud
    #ZK_HOST=""
    
    删除 ZK_HOST 前面的注释符“#”,并配置 ZooKeeper 连接:
    # Set the ZooKeeper connection string if using an external ZooKeeper ensemble
    # e.g. host1:2181,host2:2181/chroot
    # Leave empty if not using SolrCloud
    ZK_HOST="zk1:2181,zk2:2181,zk3:2181/solr"
    
  • Windows: solr.in.cmd
    REM Set the ZooKeeper connection string if using an external ZooKeeper ensemble
    REM e.g. host1:2181,host2:2181/chroot
    REM Leave empty if not using SolrCloud
    REM set ZK_HOST=
    
    删除 ZK_HOST 前面的注释标记“REM”,并配置 ZooKeeper 连接:
    REM Set the ZooKeeper connection string if using an external ZooKeeper ensemble
    REM e.g. host1:2181,host2:2181/chroot
    REM Leave empty if not using SolrCloud
    set ZK_HOST=zk1:2181,zk2:2181,zk3:2181/solr
    

如果不修改 solr.in.sh/solr.in.cmd 脚本内容的话,我们使用 bin/solr 以 SolrCloud 模式启动 solr 服务就要使用 -z 参数选项,如:bin/solr -z localhost:2181
如果使用 -z 参数则可以不使用 -cloud,只要启动多个 Solr 实例连接在同一个 ZooKeeper 集群下,这些 Solr 实例既可以构成 SolrCloud 集群。

模拟搭建 3 实例 SolrCloud 集群

本教程会在三台装有 CentOS 7 的虚拟机上建立一个 ZooKeeper 集群。建立好之后,我们会在每台虚拟机上运行 Solr 服务,并连接到 ZooKeeper 集群,从而形成一个包含三个实例的 SolrCloud 集群。

虚拟机的安装

笔者使用的是 Mac 电脑进行操作,所以需要在本地先安装 VMware 虚拟机,并安装 CentOS7 镜像服务。

NOTE:
Mac 电脑虚拟机安装 CentOS7 一直卡在安装界面
笔者本人使用的是 MacBook Pro(Apple M1 Pro)电脑,在虚拟机安装完毕后我首先使用的是官网下载的 CentOS7 的镜像,结果安装镜像的时候一直卡在安装界面:
在这里插入图片描述
在网上搜索相关解决办法,找到了一个可以用的镜像,下面提供了镜像和虚拟机的网盘地址:

VMware Fusion 13.5.0:

CentOS 7 镜像:

虚拟机安装完毕后可以通过修改 /etc/hosts 定义域名映射,如下所示:
在这里插入图片描述

虚拟机设置免密登录

在CentOS 7中设置免密登录通常涉及SSH公钥认证的使用。这种方法允许用户从一个系统(客户端)无需输入密码就能安全登录到另一个系统(服务器)。以下是设置步骤:

  1. 生成SSH密钥对:在客户端机器上,使用ssh-keygen命令生成一对SSH密钥(一个公钥和一个私钥)。如果您已经有了SSH密钥对,可以跳过这一步。

    ssh-keygen -t rsa -b 2048
    

    按照提示操作,您可以设置一个密码来保护私钥,但为实现免密登录,可以留空。

  2. 复制公钥到服务器:使用ssh-copy-id命令将公钥复制到服务器上。假设您要连接的服务器用户名是username,服务器的IP地址是server_ip

    ssh-copy-id username@server_ip
    

    这个命令会要求您输入服务器的密码。一旦完成,公钥将被添加到服务器上用户家目录中的~/.ssh/authorized_keys文件中。

  3. 测试免密登录:尝试使用SSH连接到服务器,您应该能够在不输入密码的情况下登录。

    ssh username@server_ip
    
  4. 确保SSH配置正确:确保服务器的SSH配置允许公钥认证。在服务器上,检查/etc/ssh/sshd_config文件中的以下设置:

    • PubkeyAuthentication yes
    • AuthorizedKeysFile .ssh/authorized_keys
    • 确保没有设置禁止公钥认证的规则。
  5. 重启SSH服务(如有必要):如果您更改了SSH配置文件,需要重启SSH服务。

    sudo systemctl restart sshd
    

搭建 ZooKeeper 集群

  1. 下载并解压 ZooKeeper
    首先,从 ZooKeeper 官网 下载 ZooKeeper 服务并解压。

  2. 配置 ZooKeeper
    在 ZooKeeper 的 conf 目录下,复制 zoo_sample.cfg 文件并重命名为 zoo.cfg。接着,修改配置如下:
    tickTime=2000
    initLimit=10
    syncLimit=5
    dataDir=/opt/zookeeper/data
    dataLogDir=/opt/zookeeper/log
    server.1=centos7-001:2888:3888
    server.2=centos7-002:2888:3888
    server.3=centos7-003:2888:3888
    clientPort=2181
    autopurge.snapRetainCount=3
    autopurge.purgeInterval=1
    注意:centos7-001centos7-002centos7-003 是虚拟机的局域网地址。

  3. 配置 myid 文件
    /opt/zookeeper/data 目录下,每台服务器都需要创建一个 myid 文件。第一台服务器的 myid 文件内容为 1,第二台为 2,第三台为 3

  4. 启动 ZooKeeper 服务
    配置完成后,分别在三台服务器上运行 zkServer.sh start 命令启动 ZooKeeper 服务。

  5. 配置防火墙
    如果启用了防火墙,需要开放 ZooKeeper 使用的端口:
    firewall-cmd --permanent --add-port=2181/tcp
    firewall-cmd --permanent --add-port=2888/tcp
    firewall-cmd --permanent --add-port=3888/tcp
    firewall-cmd --reload

  6. 检查集群状态
    使用 ZooInspector 工具或其他方法,从本地远程连接 ZooKeeper 集群,以确认集群状态。

下载启动 Solr 服务

  1. 下载 Solr 服务
    在一台虚拟机上,进入 /opt/solr 目录,执行以下命令来下载并解压 Solr 服务:
    wget https://archive.apache.org/dist/lucene/solr/7.7.2/solr-7.7.2-src.tgz
    tar -zxvf solr-7.7.2-src.tgz

  2. 编写启动脪本
    为了简化启动过程,我们编写了一个启动脚本,其内容如下:

    /opt/solr/solr-7.7.2/bin/solr start -a "-Djava.class.path=/data/solrplugin/* -Dsolr.log.dir=/opt/solr/logs/solr_8983_log/ -Xloggc:/opt/solr/logs/solr_8983_log/solr_gc_%t.log" -z centos7-001:2181,centos7-002:2181,centos7-003:2181/solr -h centos7-001 -s /opt/solr/solr_home_8983 -p 8983 -force
    

脚本内容解释如下:

  1. /opt/solr/solr-7.7.2/bin/solr start
    • 这部分是启动Solr的基本命令。它指定了Solr的安装路径和版本(在这个例子中是7.7.2),并且调用了bin/solr脚本的start命令来启动Solr服务。
  2. -a "-Djava.class.path=/data/solrplugin/* -Dsolr.log.dir=/opt/solr/logs/solr_8983_log/ -Xloggc:/opt/solr/logs/solr_8983_log/solr_gc_%t.log"
    • -a:这个参数用于向Solr的JVM(Java虚拟机)传递额外的参数。
    • -Djava.class.path=/data/solrplugin/*:这指定了JVM的类路径,包括/data/solrplugin/目录下的所有jar文件。
    • -Dsolr.log.dir=/opt/solr/logs/solr_8983_log/:这设置了Solr的日志目录。
    • -Xloggc:/opt/solr/logs/solr_8983_log/solr_gc_%t.log:这配置了垃圾收集日志的输出路径,%t将被替换为JVM启动时的时间戳。
  3. -z centos7-001:2181,centos7-002:2181,centos7-003:2181/solr
    • 这指定了Solr连接到ZooKeeper集群的配置,用于SolrCloud模式。centos7-001:2181, centos7-002:2181, centos7-003:2181是ZooKeeper节点和它们的端口,/solr是ZooKeeper中的chroot路径。
  4. -h centos7-001
    • 这设置了Solr服务器的主机名(在这个例子中是centos7-001)。
  5. -s /opt/solr/solr_home_8983
    • 这指定了Solr的home目录,即Solr的核心配置文件所在的位置。
  6. -p 8983
    • 这指定了Solr服务监听的端口号(在这个例子中是8983)。
  7. -force
    • 这个参数用于强制启动Solr实例,由于我们使用的是 root 账号所以这里必须加上该选项否则会出现报错。
  1. 配置 SolrHome
    /opt/solr/solr_home_8983 目录下创建 solr.xml 文件,这个文件可以从 /opt/solr/solr-7.7.2/server/solr/solr.xml 复制过来。

  2. 在其他虚拟机上重复步骤
    接下来,在另外两台虚拟机上重复上述步骤。重要的是要确保在启动脚本中正确修改主机名称。

  3. 开放端口
    为了确保 Solr 服务可以被访问,需要在防火墙中开放 8983 端口。

服务启动后,访问任一台服务器的 Solr Admin 页面。在页面上,您应该能看到 Cloud Nodes 的数量为 3,这表明 Solr 服务已成功启动并加入到集群中
在这里插入图片描述
如果需要停止 Solr 服务,可以运行 /opt/solr/solr-7.7.2/bin/solr stop -all 命令。

使用 chroot

如果你打算让你的 ZooKeeper 集群不仅服务于Solr,还要与其他系统共享,那么建议你为每个应用程序专门设置 znodes,或者创建一个只包括 Solr 文件的分层命名空间。

为每个应用程序创建 znode 后,你需要在连接 ZooKeeper 的字符串末尾加上这个 znode 的名称,这个名称也叫做 chroot。这样做是为了告诉 Solr 如何找到 ZooKeeper。
如上面 Solr 启动命令中的 centos7-001:2181,centos7-002:2181,centos7-003:2181/solr

使用bin/solr命令可以创建一个chroot:

bin/solr zk mkroot /solr -z zk1:2181,zk2:2181,zk3:2181

一旦znode被创建,它就类似于文件系统上的一个目录:Solr在ZooKeeper中存储的数据将嵌套在主数据目录下,不会与使用同一ZooKeeper集群的其他系统或进程的数据混合。内容如下所示:
在这里插入图片描述
有关此命令的更多示例,请参阅 Create a znode 部分。

其他问题

Solr Cloud 模式启动后,查看 ZK Status 服务报错

启动Solr后,查看 ZK Status,Solr 日志出现报错,如图所示:
在这里插入图片描述
报错日志如下:

2024-01-18 10:48:20.850 INFO  (qtp1016925085-20) [   ] o.a.s.s.HttpSolrCall [admin] webapp=null path=/admin/zookeeper/status params={wt=json&_=1705915545513} status=500 QTime=33
2024-01-18 10:48:20.850 ERROR (qtp1016925085-20) [   ] o.a.s.s.HttpSolrCall null:java.lang.ArrayIndexOutOfBoundsException: 1
	at org.apache.solr.handler.admin.ZookeeperStatusHandler.monitorZookeeper(ZookeeperStatusHandler.java:185)
	at org.apache.solr.handler.admin.ZookeeperStatusHandler.getZkStatus(ZookeeperStatusHandler.java:99)
	at org.apache.solr.handler.admin.ZookeeperStatusHandler.handleRequestBody(ZookeeperStatusHandler.java:77)
	at org.apache.solr.handler.RequestHandlerBase.handleRequest(RequestHandlerBase.java:199)
	at org.apache.solr.servlet.HttpSolrCall.handleAdmin(HttpSolrCall.java:736)
	at org.apache.solr.servlet.HttpSolrCall.handleAdminRequest(HttpSolrCall.java:717)
	at org.apache.solr.servlet.HttpSolrCall.call(HttpSolrCall.java:496)
	at org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:395)
	at org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:341)
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1602)
	at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:540)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:146)
	at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548)
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:257)
	at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1588)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:255)
	at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1345)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:203)
	at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:480)
	at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1557)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:201)
	at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1247)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:144)
	at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:220)
	at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:126)
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
	at org.eclipse.jetty.rewrite.handler.RewriteHandler.handle(RewriteHandler.java:335)
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
	at org.eclipse.jetty.server.Server.handle(Server.java:502)
	at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:364)
	at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:260)
	at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:305)
	at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103)
	at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:118)
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:333)
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:310)
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:168)
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:126)
	at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:366)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:765)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:683)
	at java.lang.Thread.run(Thread.java:750)

调试 Solr 源码发现实际访问 ZK 报:

mntr is not executed because it is not in the whitelist 

这条消息 “mntr is not executed because it is not in the whitelist” 指的是,在尝试执行 ZooKeeper 的 mntr (monitor) 命令时,该命令由于没有被列入白名单而未被执行。ZooKeeper 需要开启 mntrruokconf 这三个四字命令

以下是ZooKeeper的四字命令列表:

命令描述
conf获取关于服务器的详细配置信息。
cons列出所有连接到服务器的客户端的完整连接/会话的详细信息。
dump打印服务器的转储信息,包括关于服务器状态和会话的详细信息。
envi打印有关服务器环境的详细信息,如版本、系统信息等。
ruok测试服务器是否处于运行状态("imok"表示服务器正常运行)。
srst重置服务器的统计信息。
srvr打印服务器的详细信息,包括版本、状态、模式等。
stat打印有关服务器的简要信息,如接受的请求计数、连接数等。
wchs列出服务器的Watch统计信息,包括观察连接数和路径。
wchc列出服务器的Watch连接的详细信息,包括会话ID和路径。
wchp列出服务器的Watch路径的详细信息,包括连接和会话ID。
mntr显示服务器的详细健康信息,包括内存使用、延迟、请求计数等。
  • 白名单机制: 出于安全考虑,ZooKeeper 有一个四字命令的白名单机制。这意味着只有在白名单中的命令才能被执行。如果尝试执行不在白名单中的命令,就会出现这样的警告消息。

  • 处理方法: 要解决这个问题,你需要将 mntr 命令添加到 ZooKeeper 的四字命令白名单中。修改 ZooKeeper 服务的配置文件(zoo.cfg),添加或更新 4lw.commands.whitelist 配置项。例如:
    4lw.commands.whitelist=mntr, stat, ruok
    这会将 mntr, stat, 和 ruok 命令添加到白名单。
    如果开启全部的四字命令输入配置为:
    4lw.commands.whitelist=*
    修改配置文件后,需要重启 ZooKeeper 服务以使更改生效。

如果按照上面的方法发现还是有问题,应该是 Solr 和 ZK 版本兼容性的 BUG,可以参考:https://issues.apache.org/jira/browse/SOLR-13672

有两种解决方法,一个是升级 Solr 版本,另一个是降级 ZK 版本,本例中我选用的 Solr 是 7.7.2,推荐使用 ZK 版本是 3.4.13,配置启动成功后 ZK Status 页面如下:
在这里插入图片描述

新版 Solr 启动后只能在本地访问其他机器访问不了

参考:https://solr.apache.org/guide/solr/latest/deployment-guide/taking-solr-to-production.html#security-considerations
启动参数里面新加:-Dsolr.jetty.host=0.0.0.0,如下所示:

/opt/solr/solr-9.5.0/bin/solr start -a "-Dsolr.jetty.host=0.0.0.0 -Djava.class.path=/data/solr9/solrplugin/* -Dsolr.log.dir=/opt/solr/logs/solr_9083_log/ -Xloggc:/opt/solr/logs/solr_9083_log/solr_gc_%t.log" -z centos7-001:2181,centos7-002:2181,centos7-003:2181/solr9 -h centos7-001 -s /opt/solr/solr_home_9083 -p 9083 -force
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐