工程

Elasticsearch 监听最佳实践:实施内容、实施场景、原因和方法

Elasticsearch 为许多常用工具和应用提供了强大的搜索体验,从用于运营分析的仪表板,到在地图上显示距离您家最近的露台餐厅,实施场景多种多样。在所有这些实施中,应用程序和集群之间的连接都通过 Elasticsearch 客户端实现。

优化客户端与 Elasticsearch 集群之间的连接,对于最终用户的体验是极为重要的。配置一个必须连接到的节点的 URL 是 Elasticsearch 客户端中比较典型的做法。但您可以进一步优化,监听就是用于优化客户端与 Elasticsearch 集群之间连接的一种方法。

下面来介绍监听的工作原理,让大家了解什么业务场景适合使用监听,以及什么业务场景应避免使用监听。

什么是监听?

作为分布式系统,Elasticsearch 的索引存在于相互连接的多个节点中,形成一个集群。除了容错性之外,分布式系统还有一大优势,那就是数据被分片存储到多个节点中,这样一来,搜索的运行速度比通过庞大体量的单个节点进行搜索要快得多。

配置一个指向 Elasticsearch 集群中一个节点的 URL 是客户端中的典型做法。虽然这是最简单的配置,但这种设置的主要缺点是,您发出的所有请求都会发送到一个特定的协调节点。这样单个节点就要承受巨大压力,整体性能也可能会受到影响。

解决这个问题的办法是向客户端传递节点的静态列表,这样请求就会平均分配到各个节点上。

或者,您也可以启用“监听”功能。

使用静态节点列表时,不能保证节点始终正常运行。例如,如果您关闭一个节点进行升级或添加若干新节点,会发生什么情况?

如果启用监听功能,客户端将开始调用 _nodes/_all/http 终端,而响应将是集群中存在的所有节点及其 IP 地址的列表。然后,客户端将更新其连接池,以使用所有新节点,并保持集群的状态与客户端的连接池同步。请注意,即使客户端下载了完整的节点列表,常规 API 调用也不会使用只作主节点的节点。

监听功能可以解决这个发现问题。那为什么不是默认启用监听呢?问得好!

什么场景适合使用监听功能?

监听功能可能是一把双刃剑。如果尝试调用 _nodes/_all/http 终端,您会看到一个列表,其中包含了节点及其各自的终端。但有几个问题需要考虑:

  • 如果您的 Elasticsearch 集群位于自己的网络中,会发生什么情况?
  • 如果您的 Elasticsearch 集群位于负载均衡器后面会怎样?

这两个问题的答案很简单:您将得到毫无用处的 IP 地址,因为您处在不同的网络中

您可以用 Docker 自己试试。启动一个 Elasticsearch 实例(一个就够了),然后从本地计算机调用 call _nodes/_all/http。您会发现节点的 IP 地址与您刚才使用的 IP 地址不同。

使用下面的命令启动一个 Elasticsearch 实例:

docker run \ 
-p 9200:9200 \
-e "discovery.type=single-node" \
docker.elastic.co/elasticsearch/elasticsearch:7.8.0

现在,您可以使用下面的命令读取节点 IP(在下面的代码片段中,为了更轻松地读取响应,我们将使用 jq。):

curl -s localhost:9200/_nodes/_all/http | jq .nodes[].http.publish_address

最后,您可以复制在终端中打印的 IP 地址,然后尝试向其发送一个请求:

curl {ip_address}:9200 | jq .

可以看见,您不会得到成功响应。

这意味着,如果在集群位于另一个网络中时,在客户端中启用监听,客户端就会将所有新节点添加到其连接池中。这是因为它无法理解这些 IP 地址是错误的,对其中一个节点的每一次查询都会失败。

由于具有正确 IP 地址的初始节点已不在集群状态中,因此会被丢弃,很快就会出现“无活动连接”错误。

但我们可以解决这个问题。

要解决这个问题,您可以将 Elasticsearch 配置为绑定到其主机,但公布另一个主机。http.publish_host 配置选项可以帮您做到这一点。现在,在进行新配置后,尝试运行上面的 Docker 命令:

docker run \ 
-p 9200:9200 \
-e "discovery.type=single-node" \
-e "http.publish_host=localhost" \
docker.elastic.co/elasticsearch/elasticsearch:7.8.0

接着,再次运行下面的命令:

curl -s localhost:9200/_nodes/_all/http | jq .nodes[].http.publish_address

终端将显示以下内容:

"localhost/{ip_address}:9200"

如果您配置了发布主机,那么官方客户端(v7 及更高版本)就会智能地使用主机地址而不是 IP。

由此可见,在启用监听功能之前,应该了解自己的基础架构。解决 IP 地址问题的方法有很多,但没有皆大欢喜的解决方案,因为这完全取决于您的系统配置。

典型的开发设置是将 Elasticsearch 集群与客户端放在同一网络中,但在真实场景中无法复制这个方案,因为这样做会导致安全问题,而且您的基础架构可能更加复杂。您可以配置负载均衡器来处理这些 IP 地址。或者,就像 Elastic 在 Elastic Cloud 中所采取的方式,您可以让代理来处理故障节点,这样客户端就会始终将查询发送给代理,然后代理再将查询发送到相应的节点。

什么场景不适合使用监听功能?

在许多情况下,监听可能会导致一些问题,其中包括:

  • 您的客户端正在进行身份验证的 Elasticsearch 用户没有访问节点 API 的适当权限(monitoring_user 角色)。
  • 您正在与云服务提供商协作。

通常情况下,云服务提供商会将 Elasticsearch 隐藏在代理后面,这样监听操作会变得毫无用处,因为返回的地址和主机名在您的网络中可能没有任何意义。通常,这些云服务提供商会为您处理监听和池化等复杂操作,因此您不需要启用这些功能。

如果您使用的是 Elastic Cloud,官方客户端会在内部简化大多数操作(例如连接池处理),以避免在已经完成的操作上花费时间。

如前所述,在使用 Docker 或 Kubernetes 时,还会出现其他问题。除非您配置了发布主机选项,否则监听结果将无法使用。

经验之谈:如果 Elasticsearch 与客户端位于不同的网络中(或者有负载均衡器),应禁用监听功能,除非基础架构中的某些配置支持您准确地使用监听。

通过什么方式监听?

客户端提供了多种监听策略。我们来逐个分析:

在启动时监听

顾名思义,启用这个选项后,客户端将仅在初始化或首次使用时尝试执行一次监听请求。

在连接失败时监听

如果启用了这个选项,客户端就会在每次节点出现故障(即连接中断或节点无效)时尝试执行监听请求。

监听间隔

除了在启动时监听和发生故障时监听之外,定期监听也很有用,比如集群通常在高峰时段水平扩展这样的场景。应用程序或许可以查看正常运行节点的子集。但是,如果不定期进行监听,它就永远找不到在水平扩展期间添加的节点。

定制配置

在某些情况下,您可能希望对监听过程进行更精细的控制。客户端非常灵活,可让您配置定制监听终端,您也可以完全覆盖监听逻辑,提供自己的监听逻辑。

结论

启用监听功能后,您的应用程序将更具弹性,更能适应不断发生的变化。但在启用此功能之前,您应该了解自己的基础架构,以便决定采用什么样的解决方案最合适。最佳方案甚至可能是不采用监听。

如果您不想考虑监听和连接池配置,而是想使用一个简单的连接字符串,不妨试试 Elastic Cloud 上 Elasticsearch Service 的 14 天免费试用版