套件管理員內部機制

套件管理員 (CLI) 在內部使用各種 Solr API 來安裝、部署和更新套件。本文檔包含這些 API 的概述。

顯著特性

  • 零中斷部署(熱部署):應該可以在不重新啟動節點或重新載入核心的情況下安裝和更新套件,因此部署應該快速且沒有失敗的請求或過時的快取。

  • 輕鬆打包

    • 應支援標準外掛程式概念,例如,查詢剖析器、搜尋元件、請求處理常式、URP 等,而無需任何特殊的程式碼/打包變更。

    • 使用者已部署(並在生產環境中使用)的成品(包含自訂外掛程式的 jar 檔)應該是相容的,而無需重新編譯或重新打包,以便更廣泛地採用。

    • 應該支援單個 jar 套件以及多個 jar 套件。

    • 使用熟悉的/標準命名

    • 使用與 apt、dnf、homebrew 等類似的套件管理員相關的業界標準概念和術語。

類別載入器

在系統的核心,我們有類別載入器隔離。為實現此目的,系統簡化為兩層類別載入器

  • 根類別載入器,其中包含來自 Solr 類別路徑的所有 jar 檔。這需要重新啟動 Solr 節點才能變更任何內容。

  • 一組繼承自根類別載入器的具名類別載入器。具名類別載入器的生命週期與 ZooKeeper 中的套件設定相關聯。一旦修改了設定,就會重新載入對應的類別載入器,並要求元件重新載入。

套件載入安全性

套件預設為停用。使用系統屬性 -Denable.packages=true 啟動所有節點以使用此功能。

範例

bin/solr start -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 檔以及其他檔案。

套件儲存如何運作?

當檔案上傳至套件儲存時,以下情況為真:

  • 它會儲存到本機檔案系統。

  • 它會與元數據一起儲存。元數據檔案也會儲存 jar 檔案的 sha512 和簽章。

  • 叢集中的每個運作節點也會被要求下載它。

套件儲存 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.7.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  https://127.0.0.1:8983/api/cluster/files/mypkg/1.0/myplugins.jar?sig=<signature-of-jar>
  4. 驗證您的 jar 檔上傳

    curl https://127.0.0.1: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  https://127.0.0.1:8983/api/cluster/package -H 'Content-type:application/json' -d  '
    {"add": {
             "package" : "mypkg",
             "version":"1.0",
             "files" :["/mypkg/1.0/myplugins.jar"]}}'
  2. 驗證建立的套件

    curl https://127.0.0.1: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 https://127.0.0.1:8983/solr/gettingstarted/config -H 'Content-type:application/json' -d  '{
              "create-requesthandler": { "name": "/test",
              "class": "mypkg:org.apache.solr.core.RuntimeLibReqHandler" }}'
  4. 驗證元件是否已建立,並且它正在使用正確的套件版本來載入類別

    curl https://127.0.0.1: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 https://127.0.0.1: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.7.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  https://127.0.0.1:8983/api/cluster/files/mypkg/2.0/myplugins.jar?sig=
  7. 驗證它

    curl https://127.0.0.1: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  https://127.0.0.1:8983/api/cluster/package -H 'Content-type:application/json' -d  '
    {"add": {
             "package" : "mypkg",
             "version":"2.0",
             "files" :["/mypkg/2.0/myplugins.jar"]}}'
  9. 驗證外掛程式以查看是否正在使用正確的套件版本

    curl https://127.0.0.1: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 https://127.0.0.1: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),無論稍後是否將任何晚於 2.0 的版本新增至 Solr。

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