sbt启动机制、配置优化及与Intellij IDEA的集成
?
一、sbt啟動機制理解與啟動緩慢的原因分析
眾所周知,不加修改,直接使用sbt,那么sbt“啟動(launch)”會非常慢,甚至會失敗,尤其當初次運行時,本地尚無緩存,需要大量加載自身依賴文件的情況下更是如此。
主要原因是:不加修改的情況下,sbt在啟動時會使用啟動器(sbt-launch.jar)內置sbt.boot.properties文件(在bt-launch.jar內的sbt/目錄下)中所指定的構建成果物庫(build Artifact Repository)。這些缺省構建成果物庫都是國外網站,在國際網絡通道日益擁堵的今天,初次下載速度就會格外緩慢。
又因為這些配置是在sbt啟動時使用和加載,對于所有sbt項目而言,它們又是全局(global)配置,會影響到所有的sbt 項目。因此,我們需要了解這些配置及其作用,sbt-launch.jar中內置sbt.boot.properties文件中構建成果物庫的配置片段如下:
| [repositories] ? local ? local-preloaded-ivy: file:///${sbt.preloaded-${sbt.global.base-${user.home}/.sbt}/preloaded/}, [organization]/[module]/[revision]/[type]s/[artifact](-[classifier]).[ext] ? local-preloaded: file:///${sbt.preloaded-${sbt.global.base-${user.home}/.sbt}/preloaded/} ? maven-central ? sbt-maven-releases: https://repo.scala-sbt.org/scalasbt/maven-releases/, bootOnly ? sbt-maven-snapshots: https://repo.scala-sbt.org/scalasbt/maven-snapshots/, bootOnly ? typesafe-ivy-releases: https://repo.typesafe.com/typesafe/ivy-releases/, [organization]/[module]/[revision]/[type]s/[artifact](-[classifier]).[ext], bootOnly ??sbt-ivy-snapshots: https://repo.scala-sbt.org/scalasbt/ivy-snapshots/, [organization]/[module]/[revision]/[type]s/[artifact](-[classifier]).[ext], bootOnly |
其中:
庫名: 庫配置項
各部分如何配置如下:
- 庫名,是為了跟蹤調試時給使用者查看所用,使用者可以根據方便自己的記憶來進行設置,但庫名不能重復。
- 庫配置,給出了庫所在的位置(Url),成果物文件所在的路徑布局(Layout)格式,以及這個庫在什么階段使用。格式為:
? ? ? ?位置Url, 庫文件路徑布局(Layout)格式, 使用階段指示符
其中:
? ?位置Url必須給出。
? 庫文件路徑布局(Layout)格式的用途是給Sbt將想要下載的庫文件描述定義(比如具體依賴的成果物的組織、名字、版本等)格式 化成滿足Maven或Ivy成果物庫中的路徑布局(layout)的路徑(path)信息,并與與庫的Url地址結合,生成具體的依賴相關文件(比如pom\ivy文件和Jar文件)的完整下載地址。所謂庫文件路徑布局(Layout),對于庫服務器而言,就是存儲成果物文件的存儲路徑安排。對于訪問者而言,就對成果物文件在庫服務器URL下的訪問路徑規約。Sbt缺省的布局格式用于格式化maven2庫的路徑,缺省路徑布局格式如下:
[organization]/[module]/[revision]/[type]s/[artifact](-[classifier]).[ext]
需要注意的是,有一些scala寫的sbt插件成果物的版本號中包括了scala版本號和sbt版本號,sbt需要用scala版本變量、sbt版本變量并與成果物自身的版本號一起來生成完整的成果物版本號,其[revision]格式會略有不同,完整布局(layout)格式如下:
[organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext]
使用階段指示符用來指出該庫僅在“sbt啟動期間”使用,還是后續加載項目(project)所用依賴時一直使用,該指示符用bootOnly來指示。bootOnly表明該庫只在sbt啟動過程中使用,用來下載sbt本身(或所嵌入IDE)所需的插件。在sbt成功啟動后,我們可以用sbt命令查看sbt當前所使用的庫。其中,show? fullResolvers 命令能夠查看包括在項目(project)的build.sbt文件中用resolvers配置的項目級構建成果物庫,和全局級(global)的構建成果物庫(比如,上面所提到的sbt.boot.properties文件所配置的);而show resolvers命令則用來列出項目級構建成果物庫。sbt在加載依賴(dependency)時,會按照配置文件中出現的順序依次地嘗試從所配置的庫來加載依賴文件 。如果某個比較慢的遠程庫在排在前面,就會使得整個加載速度變慢,如果遠程庫出現了超時,則會從下一個遠程庫中加載,如果這個庫加載速度較快,那么整個加載過程就會變快。在網絡不穩定時(比如國外網站超時機率高)的時候,就會出現某次啟動加載速度快,某次啟動加載速度慢的情況。不明白這種機制,會導致我們的配置某次好像很成功,啟動加載速度很快,但給別人用的時候就又變慢了的情況。
上面我們介紹了sbt構建成果物庫在啟動時(也是全局)的配置,值得注意的是,sbt在該配置段中設置了兩個“內置的”配置條目,就是在上面配置片段中的local和maven-central,通過show? fullResolvers 命令可以看到:
maven-central配置條目所代表的完整配置是:
public: https://repo1.maven.org/maven2/
它指向了Maven中央倉,它不是bootOnly,因此在sbt啟動和后續所有項目中都會用到,因此,因為前面所說的原因,忘記刪除這個配置條目,會導致國內開發者sbt啟動和后續加載依賴出現極為緩慢的可能。
Local配置條目所代表的整配置是:
FileRepository(local, Patterns(ivyPatterns=Vector(${ivy.home}/local/[organisation]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)([branch]/)[revision]/[type]s/[artifact](-[classifier]).[ext]), artifactPatterns=Vector(${ivy.home}/local/[organisation]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)([branch]/)[revision]/[type]s/[artifact](-[classifier]).[ext]), isMavenCompatible=false, descriptorOptional=false, skipConsistencyCheck=false), FileConfiguration(true, None))
看起來比較嚇人,實際上不復雜,它表明該庫名為local,其所指的庫位置位于${ivy.home}配置變量所表示的ivy home目錄下的local子目錄,如果不修改ivy home配置,缺省ivy home位置就是操作系統登錄賬號的home目錄下的 .ivy2目錄,如果不存在,sbt會自動創建,對于linux和mac os來說,ivy home路徑就是 $HOME/.ivy2/,而local庫的位置則是:$HOME/.ivy2/local,這個local子目錄需要使用者自己創建,再在其中放入所有本地依賴庫。sbt與intellij IDEA進行配合使用時,我們會用到本地依賴庫。
如果您對下載和解析依賴文件感興趣,請從以下官方文檔獲得更多信息:
https://www.scala-sbt.org/1.x/docs/Resolvers.html
https://www.scala-sbt.org/1.x/docs/Proxy-Repositories.html
?
二、啟動緩慢的解決辦法
1.總體思路
讓sbt在“啟動(launch)時”只使用國內的構建成果物庫(build Artifact Repository)鏡像服務器,比如:
阿里云: https://maven.aliyun.com/repository/public
或華為云:https://mirrors.huaweicloud.com/repository/maven/
或者,讓SBT在啟動時使用開發團隊自己的搭建的構建成果物庫,即所謂的“私服”。用來搭建“私服”的軟件產品包括收費的商業產品sonatype nexus或JFrog Artifactory,以及開源免費的apache Archiva等,如何搭建私服,本文不再詳述。
2.實現方法
上述思路的實現,需要更改sbt的啟動設置,如果前面所述,這些設置對所有sbt項目都有作用,也就是修改全局設置,sbt官方文檔:
https://www.scala-sbt.org/1.x/docs/Launcher-Configuration.html
給出了三種修改sbt 啟動設置的方式,分別是:
sbt? -Dsbt.override.build.repos=true? -Dsbt.repository.config=/.repositories
這三種方式的優先級依次升高,對我而言,比較喜歡第三種方式,主要是它的優先級高,而且比較靈活。第一種方式的缺點是需要修改sbt-launch.jar文件,一旦sbt 本身升級,就需要重新制作sbt-launch.jar文件,比較麻煩。第二種方式的缺點是sbt.boot.properties不能設定所有需要的全局配置,比如,repository服務器的身份認證信息就無法用sbt.boot.properties文件來設置。
那么使用第三種方式是不是意味著每次使用sbt都要在命令行中輸入一串長長的配置參數呢?答案是樂觀的。我們可以在sbt安裝目錄下的conf/目錄中的“sbt運行參數配置文件”中給出這些自定義的配置參數,而且還可以給出更多的運行配置選項。這樣,在命令行中輸入sbt時,就會自動按照這些設置運行。在windows系統中,這個配置文件是的名稱是sbtconfig.txt,在linux和mac os系統中,這個配置文件的名稱是sbtopts。
順便說一下,可以在sbt運行工作目錄的日志中查看sbt的啟動運行日志文件來查看這些運行配置是否生效,缺省情況下,這個啟動運行日志文件的全路徑為:
/${操作系統登錄用戶Home目錄}/.sbt/boot/update.log
下面給出的是sbtopts中部分sbt運行參數配置示例:
| #允許覆蓋內置的repositories -Dsbt.override.build.repos=true ? #給出自定義repositories配置文件 -Dsbt.repository.config=/Users/Shared/App4Deve/sbt/conf/.repositories |
如果理解了本文第一部分中的內容,剩下的工作很簡單,就是創建一個僅存放[repositories]配置段的文件,文件名沒有要求,下面給出該文件的一個示例:
| [repositories] #國內maven庫鏡像的本地“私服”的代理 local-mavenRepo-server: http://localhost:8088/artifactory/aliyun/ #國內maven庫鏡像 maven-china: https://maven.aliyun.com/repository/public |
如果你所使用的“私服”需要身份認證,那么將私服的身份認證信息存放在一個文件中,“身份認證信息文件”的名稱沒有要求,只要在“sbt運行參數配置文件(sbtopts或sbtconfig.txt文件)”中指定該身份認證信息文件位置的全路徑即可,此時的“sbt運行參數配置文件”內容如下:
| #私服的身份認證配置文件位置 -Dsbt.boot.credentials=/Users/Shared/App4Deve/sbt/conf/credentials.sbt ? #允許覆蓋內置的repositories -Dsbt.override.build.repos=true ? #給出自定義repositories配置文件 -Dsbt.repository.config=/Users/Shared/App4Deve/sbt/conf/.repositories |
而“身份認證信息文件”的內容則很簡單,示例如下:
| realm=Artifactory Realm host=localhost user=developer password=123#@! |
此時,sbt 已經可以訪問“私服”,但還需要注意以下幾點:
java.net.SocketException: Broken pipe (Write failed)
其主要原因是,如果SBT對私服進行訪問時,采用了并行請求的訪問機制,此時nexus的身份認證尚未完成,就處理上傳成果物文件請求,會導致并行上傳失敗,出現上述的 Broken pipe錯誤。問題及解決方法,詳見:https://support.sonatype.com/hc/en-us/articles/360000228868-Artifact-uploads-fail-with-broken-pipe-errors
| #關閉gigahorse,防止nexus 的Broken pipe錯誤 -Dsbt.gigahorse=false ? #私服的身份認證配置文件位置 -Dsbt.boot.credentials=/Users/Shared/App4Deve/sbt/conf/credentials.sbt ? #允許覆蓋內置的repositories -Dsbt.override.build.repos=true ? #給出自定義repositories配置文件 -Dsbt.repository.config=/Users/Shared/App4Deve/sbt/conf/.repositories |
但這里還有一個簡單粗暴的解決方法,就是關閉sbt的并行上傳機制。就是“sbt運行參數配置文件”中設置sbt 的gigahorse參數為false。此時的“sbt運行參數配置文件”如下所示:
以上設置完成后,sbt 的啟動運行就會很快,尤其在使用私服的情況下會更快。簡單總結為兩步:
三、Intellj IDEA中sbt的使用設置
如果我們學會了如何更改sbt的設置,并在控制臺(console)的命令行下驗證了sbt的設置的成功。那么在IDEA對sbt的設置思路也是一樣的,主要是找到如何設置這些參數的地方,但是還存在其他的一些問題,導致sbt 在IDEA中無法運行。
首先,我們要知道,IDEA中已經打包了(bundled)某個版本的sbt,不同IDEA的版本打包了不同版本的sbt,目前我所使用的IDEA版本是2019.3,該版本IDEA所打包是sbt1.2.8,而我下載sbt則是較新的sbt1.3.8。為了在操作系統控制臺(console)中所使用sbt與IDEA中所使用的sbt能夠利用同一套配置好的構建成果物庫(repositores)的配置文件和“私服”的認證(credentials)信息文件,我們需要更改IDEA的配置1。具體操作菜單路徑如下:
Preferences|Build,Execution,Deployment|Build Tools|sbt
此時看到的IDEA設置界面如下:
轉存失敗重新上傳取消轉存失敗重新上傳取消
?
| 1.IEDA更改配置有兩個級別,一個是全局(global)級別配置,一個是項目(project)級別設置。全局級別配置用來設置所有項目都會用到的通用配置項(General Settings),全局級別配置相當于這些通用配置項在項目級別配置中的缺省值。項目級別配置中這些通用配置項的配置值會覆蓋全局級別配置,項目級別配置中還有項目專有的配置項。無論全局級別配置還是項目級別配置,二者都是通過設置Preferencs開始,區別在于設置Preferences的時機,全局配置是未打開任何工程所做的Preferences配置,而項目級別Preferences則是打開具體項目后所的Preferences配置。 |
在General Settings|Lancher(sbt-lanuch.jar)下,選擇Custom,給定所要使用的sbt的sbt-launch.jar文件,這就完成了對sbt自定義選擇設置。
?
其次,我們需要了解的是,“sbt運行參數配置文件”——前面所說的sbtconfig.txt文件(windows)或sbtopts文件(linux\mac os)中所配置的sbt 運行參數對于在IDEA中運行sbt并不起作用,因為在操作系統控制臺(console)中運行sbt 命令實際上是一個shell腳本,現在把它叫做sbt 命令腳本它調用了操作系統JVAV_HOME中的JRE來運行sbt-launch.jar,即:java -jar sbt-launch.jar? [運行參數] ,而[運行參數]則是sbt 命令腳本從“sbt運行參數配置文件”中讀取出來的。在IDEA中,是由IDEA使用配置的JRE來運行sbt-launch.jar文件,因此需要在VM參數中指定先前在“sbt運行參數配置文件”所指定的運行參數。具體操做菜單路徑還是:
Preferences|Build,Execution,Deployment|Build Tools|sbt
界面如下:
點擊黃色小圈部分,可以展開一個更大的輸入窗口來輸入運行參數,示例如下:
當配置到這一步,你可能會認為,IDEA中使用sbt與在操作系統控制臺中使用sbt是一樣的效果。但現實沒有那么豐滿,你會發現有些版本的IDEA中(目前為止,IDEA 2019.1.3以后版本均會出現問題)仍然使用不了sbt,仔細閱讀錯誤提示信息會提示org.jetbrains組織所開發三個sbt插件(plugin)下載失敗。這三個插件分別是:
| 插件 | 作用 |
| sbt-idea-compiler-indices | 幫助IDEA對.calss文件建立索引,提高性能 |
| sbt-idea-shell | 在IDEA中提供sbt操作的命令行控制臺 |
| sbt-structure-extractor | 幫助IDEA導入sbt項目時進行目錄結構轉換 |
其中,后兩個是IDEA所必須的。
Jetbrains目前還沒有把這三個插件適應不同版本scala和sbt的所有版本成的插件果物都放在中央倉中,所以無論如何都無法下載到這些插件的合適版本(適應你所使用的scala與sbt 版本)。因此,Jetbrains把這些插件IDEA打包在一起,安裝在本地磁盤上,IDEA在運行sbt前,通過其他方式設定了sbt repositories條目(比如,在classpath中配置sbt啟動設置,這一點我沒有深入研究),指向了這些插件所在的本地磁盤目錄,這樣,IDEA運行sbt時就能夠找到適合IDEA所打包(bundled)的sbt版本所使用的插件。但是,由于我們通過VM參數 -Dsbt.override.build.repos=true 的設置,覆蓋了sbt原有的repositories設置,此時,IDEA運行sbt就會找不到這些本地插件所在的repository,又無法遠程下載,故而會出現上述的錯誤。這是IDEA已知的問題(issue),感興趣可以看Jetbrains官方文檔:https://youtrack.jetbrains.com/issue/SCL-15261 。
知道了這個原因后,我們就很好解決這個問題,那我們就有多種方式來解決這個問題,包括但不限于:
- 修改我們的repositories配置文件,添加一個repository配置項,重新指向IDEA所安裝的本地插件庫2。
- 如果repositories配置文件中有local這個內置配置條目,則不需要修改repositories配置文件,將插件庫直接拷貝到ivy HOME路徑下的local目錄中即可。
-
將插件庫拷貝到一個新目錄中,并修改repositories配置文件,增加一個repository配置項,指向這個新目錄。2.不同操作系統中,安裝位置可能不同,這里不在一一列舉。這個插件庫的目錄名稱為:org.jetbrains,通過搜索找到該目錄即可。如果需要拷貝,必須將org.jetbrains目錄整體拷貝,不能只拷貝其下的子目錄,否則將無法匹配layout格式,因為丟失了成果物開發組織的路徑信息(這里是org.jetbrains)。
?
?
| [repositories] #idea所需的org.jetbrains本地插件庫 local-ideaSbtPluginRepo-path: file:Users/learn/.sbtlocal/local/,[organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext] #國內maven庫鏡像的本地“私服”的代理 local-mavenRepo-server: http://localhost:8088/artifactory/aliyun/ #國內maven庫鏡像 maven-china: https://maven.aliyun.com/repository/public |
為展示知識點,我采用最后一種方式,此時repositories配置文件示例如下:
?
在這個示例中,我把org.jetbrains插件庫拷貝到了如下目錄:
/Users/learn/.sbtlocal/local/
??? 到了這一步,在IDEA中就可以啟動運行sbt了嗎?答案可沒那么樂觀,intellj IDEA作為sbt的集成者,其步伐總會落后一步,最大的可能就是因為IDEA所帶的sbt-idea-compiler-indices插件與你當前所使用的scala與sbt 版本不匹配而導致sbt在intellj中無法工作。好在這個插件的功能不是必須的,可以在IDEA中關閉。關閉的操作路徑及界面如下:
Preferences|Build,Execution,Deployment|Compiler|Scala Compiler|ByteCode Indices
將上述界面中紅圈所示的index ?.class files選項關閉即可。
至此,我解決了我目前所遇到的sbt的所有棘手問題
-----------全文完。
總結
以上是生活随笔為你收集整理的sbt启动机制、配置优化及与Intellij IDEA的集成的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在线制作html模板,快速建站 20个设
- 下一篇: 对SAAS浅谈