包管理器内部

软件包管理器 (CLI) 在内部使用各种 Solr API 来安装、部署和更新软件包。本文档包含了这些 API 的概述。

显著功能

  • 零中断部署(热部署):应该能够在不重新启动节点或重新加载内核的情况下安装和更新软件包,因此部署应该快速且没有失败的请求或过期的缓存。

  • 轻松打包

    • 应支持标准插件概念,例如查询解析器、搜索组件、请求处理程序、URP 等,而无需任何特殊代码/打包更改。

    • 用户已经部署(并且在生产中使用)的工件(包含自定义插件的 jar)应兼容,无需重新编译或重新打包,以便更广泛地采用。

    • 应支持单个 jar 软件包以及多个 jar 软件包。

    • 使用熟悉的/标准命名

    • 使用围绕软件包管理器的行业标准概念和术语,类似于 apt、dnf、homebrew 等。

类加载器

在系统的核心,我们有类加载器隔离。为了实现这一点,系统被简化为两个分层的类加载器

  • 根类加载器,其中包含来自 Solr 类路径的所有 jar。这需要重新启动 Solr 节点才能更改任何内容。

  • 一组从根类加载器继承的命名类加载器。命名类加载器的生命周期与 ZooKeeper 中的软件包配置相关联。一旦修改了配置,相应的类加载器就会重新加载,并要求组件重新加载。

软件包加载安全性

软件包默认情况下处于禁用状态。使用系统属性 -Denable.packages=true 启动所有节点以使用此功能。

示例

bin/solr -c -Denable.packages=true

上传您的密钥

软件包二进制文件必须使用您的私钥签名,并确保您的公钥已发布在软件包存储的可信存储中。

示例

openssl genrsa -out my_key.pem 512
# create the public key in .der format
openssl rsa -in my_key.pem -pubout -outform DER -out my_key.der
# upload key to package store
bin/solr package add-key my_key.der

软件包存储

软件包存储是一个分布式文件存储,它可以将任意文件存储在文件系统中。

  • 这是一个基于完全复制文件系统的存储库。

  • 它位于每个 Solr 节点上的 <solr.home>/filestore 中。

  • 每个条目是一个文件 + 元数据。元数据命名为 .<filename>.json

  • 元数据文件包含文件的 sha256 和签名。

  • 用户无法创建以句点 (.) 开头的文件。

  • 它与文件的内容类型无关。您可以存储 jar 文件以及其他文件。

Package Store 如何工作?

当文件上传到 Package Store 时,以下情况成立

  • 它被保存到本地文件系统。

  • 它与元数据一起保存。元数据文件也存储 jar 文件的 sha512 和签名。

  • 集群中的每个活动节点都会被要求下载它。

Package Store API

端点是

  • 每个节点中的 PUT /api/cluster/files/{full/path/to/file}

  • GET /api/node/files/{full/path/to/file} 下载文件

  • GET /api/node/files/{full/path/to/file}?meta=true 获取文件的元数据

  • GET /api/node/files/{full/path/to/} 获取 /full/path/to 中的文件列表

对您的制品进行签名

使用以下步骤上传使用您的公钥签名的 jar

  1. 如果您没有带有插件的 jar 文件,请从 GitHub 下载一个示例

    curl -o runtimelibs.jar   -LO https://github.com/apache/solr/blob/releases/solr/9.5.0/solr/core/src/test-files/runtimecode/runtimelibs.jar.bin?raw=true
  2. 使用您的私钥对 jar 进行签名

    openssl dgst -sha1 -sign my_key.pem runtimelibs.jar | openssl enc -base64 | sed 's/+/%2B/g' | tr -d \\n | sed
  3. 上传您的 jar 和签名,用前一个命令的输出替换 sig 参数

    curl --data-binary @runtimelibs.jar -X PUT  http://localhost:8983/api/cluster/files/mypkg/1.0/myplugins.jar?sig=<signature-of-jar>
  4. 验证您的 jar 上传

    curl http://localhost:8983/api/node/files/mypkg/1.0?omitHeader=true
    {
      "files":{"/mypkg/1.0":[{
      "name":"myplugins.jar",
      "timestamp":"2019-11-11T07:36:17.354Z",
      "sha512":"d01b51de67ae1680a84a813983b1de3b592fc32f1a22b662fc9057da5953abd1b72476388ba342cad21671cd0b805503c78ab9075ff2f3951fdf75fa16981420",
      "sig":["elNjhmWIOgTgbAzeZ+OcwR42N7vqL6Ig9eAqn4YoP2thT7FJuhiaZuCPivjMkD682EBo9gveSCTyXIsZKjOCbQ=="]}]}}

软件包

软件包具有以下属性

  • 一个唯一名称

  • 一个或多个具有以下属性的版本

    • version:版本字符串

    • files:来自软件包存储库的文件数组

对于软件包定义中的每个软件包/版本,都有一个唯一的 SolrResourceLoader 实例。这是 CoreContainer 资源加载器的子级。

Solr 不要求版本字符串遵循任何特定格式 - 它可以是任意字符串,甚至可以是空字符串。

packages.json

软件包配置存在于 ZooKeeper 中名为 packages.json 的文件中。在任何给定的时刻,我们可以在软件包配置中拥有给定软件包的多个版本。系统将始终使用最新版本。版本按其值按字典顺序排序,最大的字符串被认为是最新的。

版本字符串的字典顺序意味着对于具有版本 1.2.01.9.01.11.0 的软件包,Solr 会选择 1.9.0 作为最新版本。

例如

{
 "packages" : {
   "mypkg" : {
     "name": "mypkg",
     "versions": [
       {"version" : "0.1",
       "files" : ["/path/to/myplugin/1.1/plugin.jar"]
       },
       {"version" :  "0.2",
       "files" : ["/path/to/myplugin/1.0/plugin.jar"]
       }]}}}

API 端点

  • GET /api/cluster/package 获取软件包列表

  • POST /api/cluster/package 编辑包

    • add 命令:添加包的版本

    • delete 命令:删除包的版本

如何升级?

使用 add 命令添加高于当前版本的新版本。

如何降级?

使用 delete 命令删除最高版本,并选择次高版本。

并行使用多个版本

我们在集合配置中使用 params.json 存储它使用的包版本。默认情况下,它为 $LATEST

{"params":{
 "PKG_VERSIONS": {
   "mypkg": "0.1", (1)
   "pkg2" : "$LATEST", (2)
 }}}
1 对于 mypkg,无论是否有较新版本可用,都使用版本 0.1
2 对于 pkg2,使用最新版本。这是可选的。默认值为 $LATEST

params.json 中的包版本实际上指示 Solr 选择版本不高于提供值的最大版本包。

因此,在上面的示例中,如果 mypkg 唯一可用的版本是 0.010.2,则将使用版本 0.01

工作流

  • 添加包的新版本。

  • 包加载器加载类,并通知每个插件持有者新版本可用。

  • 它检查是否应该使用特定版本,忽略更新。

  • 如果没有,则重新加载插件。

在插件中使用包

任何类名都可以使用包名作为前缀,例如 mypkg:fully.qualified.ClassName,Solr 将使用包的最新版本从中加载类。从包加载的插件不能依赖于核心级别的类。

solrconfig.xml 中的插件声明
<requestHandler name="/myhandler" class="mypkg:full.path.to.MyClass">
</requestHandler>

完整的操作示例

  1. 创建包

    curl  http://localhost:8983/api/cluster/package -H 'Content-type:application/json' -d  '
    {"add": {
             "package" : "mypkg",
             "version":"1.0",
             "files" :["/mypkg/1.0/myplugins.jar"]}}'
  2. 验证已创建的包

    curl http://localhost:8983/api/cluster/package?omitHeader=true
      {"result":{
        "znodeVersion":0,
        "packages":{"mypkg":[{
              "version":"1.0",
              "files":["/mypkg/1.0/myplugins.jar"]}]}}}
  3. 此时包应该可以使用了。接下来,从包中在集合中注册插件。请注意应用于 class 属性的 mypkg: 前缀。通过编辑 solrconfig.xml 也可以实现相同的效果

    curl http://localhost:8983/solr/gettingstarted/config -H 'Content-type:application/json' -d  '{
              "create-requesthandler": { "name": "/test",
              "class": "mypkg:org.apache.solr.core.RuntimeLibReqHandler" }}'
  4. 验证组件是否已创建,并且它使用正确的包版本加载类

    curl http://localhost:8983/solr/gettingstarted/config/requestHandler?componentName=/test&meta=true&omitHeader=true
    {
      "config":{"requestHandler":{"/test":{
            "name":"/test",
            "class":"mypkg:org.apache.solr.core.RuntimeLibReqHandler",
            "_packageinfo_":{
              "package":"mypkg",
              "version":"1.0",
              "files":["/mypkg/1.0/myplugins.jar"]}}}}}
  5. 测试请求处理程序

    curl http://localhost:8983/solr/gettingstarted/test?omitHeader=true
    {
      "params":{
        "omitHeader":"true"},
      "context":{
        "webapp":"/solr",
        "path":"/test",
        "httpMethod":"GET"},
      "class":"org.apache.solr.core.RuntimeLibReqHandler",
      "loader":"java.net.FactoryURLClassLoader"}
  6. 更新组件版本。获取新版本的 jar,签名并上传

    curl -o runtimelibs3.jar   -LO https://github.com/apache/solr/blob/releases/solr/9.5.0/solr/core/src/test-files/runtimecode/runtimelibs_v3.jar.bin?raw=true
    
    openssl dgst -sha1 -sign my_key.pem runtimelibs.jar | openssl enc -base64 | sed 's/+/%2B/g' | tr -d \\n | sed
    
    curl --data-binary @runtimelibs3.jar -X PUT  http://localhost:8983/api/cluster/files/mypkg/2.0/myplugins.jar?sig=
  7. 验证

    curl http://localhost:8983/api/node/files/mypkg/2.0?omitHeader=true
    {
      "files":{"/mypkg/2.0":[{
            "name":"myplugins.jar",
            "timestamp":"2019-11-11T11:46:14.771Z",
            "sha512":"60ec88c2a2e9b409f7afc309273383810a0d07a078b482434eda9674f7e25b8adafa8a67c9913c996cbfb78a7f6ad2b9db26dbd4fe0ca4068f248d5db563f922",
            "sig":["ICkC+nGE+AqiANM0ajhVPNCQsbPbHLSWlIe5ETV5835e5HqndWrFHiV2R6nLVjDCxov/wLPo1uK0VzvAPIioUQ=="]}]}}
  8. 添加包的新版本

    curl  http://localhost:8983/api/cluster/package -H 'Content-type:application/json' -d  '
    {"add": {
             "package" : "mypkg",
             "version":"2.0",
             "files" :["/mypkg/2.0/myplugins.jar"]}}'
  9. 验证插件以查看是否使用包的正确版本

    curl http://localhost:8983/solr/gettingstarted/config/requestHandler?componentName=/test&meta=true&omitHeader=true
    {
      "config": {
        "requestHandler": {
          "/test": {
            "name": "/test",
            "class": "mypkg:org.apache.solr.core.RuntimeLibReqHandler",
            "_packageinfo_": {
              "package": "mypkg",
              "version": "2.0",
              "files": [
                "/mypkg/2.0/myplugins.jar"
              ]
            }}}}}
  10. 测试插件

    curl http://localhost:8983/solr/gettingstarted/test?omitHeader=true
    {
      "params": {
        "omitHeader": "true"
      },
      "context": {
        "webapp": "/solr",
        "path": "/test",
        "httpMethod": "GET"
      },
      "class": "org.apache.solr.core.RuntimeLibReqHandler",
      "loader": "java.net.FactoryURLClassLoader",
      "Version": "2"
    }

    请注意,Version 值为 "2",这意味着插件已更新。

如何避免自动升级

任何集合中使用的默认版本始终是最新版本。但是,在 params.json 中设置每个集合的属性可确保集合使用相同的包版本(即版本 2.0),无论稍后添加到 Solr 的版本是否高于 2.0

curl http://localhost:8983/solr/gettingstarted/config/params -H 'Content-type:application/json'  -d '{
  "set":{
    "PKG_VERSIONS":{
      "mypkg":"2.0"
      }
  }}'