【发布时间】:2017-01-17 00:39:07
【问题描述】:
当相同的应用程序代码无法在 SolrCloud 中索引没有唯一键(应该由 SOLR 自动生成)的文档并在独立 SOLR 实例(甚至在云模式下,但从其中一个副本的 Web 界面)。差异显然只存在于客户端(CloudSolrClient 与 HttpSolrClient)和 SOLR URL(Zokeeper 主机名+端口与独立 SOLR 实例主机名和端口)之间。
我正在使用 SOLR 5.1。在云模式下,我有 1 个分片和 3 个副本。 Documentation states:
架构默认值和 copyFields 不能用于填充 uniqueKey 字段。您可以使用 UUIDUpdateProcessorFactory 自动生成 uniqueKey 值。
因此,我已将我的 uniqueKey 字段添加到架构中:
<fieldType name="uuid" class="solr.UUIDField" indexed="true" />
...
<field name="id" type="uuid" indexed="true" stored="true" required="true" />
...
<uniqueKey>id</uniqueKey>
然后我将 updateRequestProcessorChain 添加到我的 solrconfig 中:
<updateRequestProcessorChain name="uuid">
<processor class="solr.UUIDUpdateProcessorFactory">
<str name="fieldName">id</str>
</processor>
<processor class="solr.RunUpdateProcessorFactory" />
</updateRequestProcessorChain>
并将其设为 UpdateRequestHandler 的默认值:
<initParams path="/update/**">
<lst name="defaults">
<str name="update.chain">uuid</str>
</lst>
</initParams>
从其中一个副本的 Web 界面添加具有 null/不存在 id 的新文档可以正常工作,就像在我的应用程序中以独立模式(非云)使用 SOLR 时一样。虽然只有当我使用 SolrCloud 并从我的应用程序中添加文档(使用 SolrJ 中的 CloudSolrClient)时,它会失败并显示“org.apache.solr.client.solrj.SolrServerException: org.apache.solr.client.solrj.impl.HttpSolrClient$ RemoteSolrException:文档缺少必填的 uniqueKey 字段:id"
所有其他操作(如 ping 或搜索文档)在任一模式(独立或云)下都可以正常工作。
有没有人遇到过同样的行为?这里有什么解决办法?
调查(即更多细节):
-
在独立模式下显然更新请求是:
POST standalone_host:port/solr/collection_name/update?wt=json -
在SOLR云模式下,从一个副本的Web界面添加文档时,更新请求是(通过检查Web界面发出的调用发现):
POST replica_host:port/solr/collection_name_shard1_replica_1/update?wt=json
在这两种情况下,有效负载类似于:
{
"add": {
"doc": {
.....
},
"boost": 1.0,
"overwrite": true,
"commitWithin": 1000
}
}
如果使用CloudSolrClient,会出现以下情况(通过调试发现):
-
使用 ZK 和一些逻辑,构建副本的 URL 列表如下所示:
[http://replica_1_host:port/solr/collection_name/, http://replica_2_host:port/solr/collection_name/, http://replica_3_host:port/solr/collection_name/] -
这段代码叫做:
LBHttpSolrClient.Req req = new LBHttpSolrClient.Req(request, theUrlList); LBHttpSolrClient.Rsp rsp = lbClient.request(req); return rsp.getResponse();第二行失败并出现异常。
如果要进一步调试第二行,它最终会调用 HttpClient.execute(来自 HttpSolrClient.executeMethod):
POST http://replica_1_host:port/solr/collection_name/update?wt=javabin&version=2 HTTP/1.1
POST http://replica_2_host:port/solr/collection_name/update?wt=javabin&version=2 HTTP/1.1
POST http://replica_3_host:port/solr/collection_name/update?wt=javabin&version=2 HTTP/1.1
第一个请求返回 400 Bad Request,副本 1 在日志中记录“文档缺少必需的 uniqueKey 字段:id”。
有趣的是,当我使用 POSTMAN(但使用 JSON 而不是二进制有效负载)执行相同的请求时,它可以工作!我在这里做错了吗?我认为这绝对是请求的方式...
更新:
我使用本地代理来查看我的应用程序发送的这 2 个请求的不同之处,以便了解那里有什么不同。看起来唯一的区别是内容类型。在云模式下,POST 文档的有效负载以“application/javabin”的形式发送,而在独立模式下,它以“application/xml; charset=UTF-8”的形式发送。其他一切都是一样的。第一个请求的结果是 400,而第二个是 200。我认为这可能是 SolrJ/SOLR 错误,因此提交了ticket for that。将保持此线程更新。
【问题讨论】:
-
会不会是配置没有传播到所有节点?那么当您在云模式下运行时,接收文档的服务器没有定义更新链?此外,在运行 in Cloud mode, be aware of any specific issues 时 - 例如日志/分发/更新步骤。
-
不,我已经检查过了。所有节点的配置都是完美的。另外,我可以看到对同一节点的请求失败,即第一个副本。如果请求是从 Web 或非云 SolrClient 发送的,则可以完美处理请求的同一节点。