使用SparkJava和Graal的本机微服务
使用SparkJava編寫的微服務(wù)只是使用標(biāo)準(zhǔn)Java庫的普通Java代碼。 沒有注釋魔術(shù),只有代碼。 這種簡單的編程風(fēng)格的優(yōu)點在于,它很簡單。 非常簡單,以至于Graal本機(jī)編譯器無需閃爍就可以對其進(jìn)行編譯 ,這在例如Spring之類的更復(fù)雜的框架中目前非常困難。
SparkJava / Graal組合本身就很有趣,人們對此的體驗也開始 出現(xiàn) 。 此外,作為Java庫,應(yīng)該可以從其他基于JVM的語言中使用它,而我想知道Graal將如何應(yīng)對。 實際上,事實證明這很簡單,在本文中,我們將看到為Java,Kotlin甚至Clojure構(gòu)建本機(jī)微服務(wù)二進(jìn)制文件非常容易。
入門
如果您還沒接觸過Graal,我建議您訪問他們的網(wǎng)站 ,看看它提供了什么。 在這里,我們使用的是本機(jī)編譯功能,但實際上這只是表面。
要首先使用Graal,您需要安裝最新版本的Graal SDK。 撰寫本文時為1.0.0-rc9 。 我使用SdkMan做到了 :
sdk install java 1.0.0-rc9-graal從那時起
sdk use java 1.0.0-rc9-graal然后創(chuàng)建一個基本的Gradle項目并添加最小依賴項:
dependencies {compile "com.sparkjava:spark-core:2.7.2"compile "org.slf4j:slf4j-simple:1.7.13" }(我假設(shè)您已經(jīng)熟悉Gradle,如果愿意的話,可以使用Maven進(jìn)行 。請注意,選擇的Slf4j實現(xiàn)與SparkJava所需的版本匹配非常重要。)
對于SparkJava,微服務(wù)端點本質(zhì)上是lambda表達(dá)式形式的路徑或回調(diào)之間的綁定或route 。 這是我們將用作基礎(chǔ)的標(biāo)準(zhǔn)“ hello world”示例。 當(dāng)然,現(xiàn)實世界中的服務(wù)將利用請求和響應(yīng)對象。 請參閱文檔以獲取更多詳細(xì)信息。
import static spark.Spark.*;public class HelloWorld {public static void main(String[] args) {get("/sayHello", (req, res) -> "Hello world!");} }要將其作為命令行程序運行,將所有依賴項一起復(fù)制到同一目錄中非常方便。 我們也可以使用Gradle做到這一點。
task copyDependencies(type: Copy) {from configurations.defaultinto 'build/libs'shouldRunAfter jar }assemble.dependsOn copyDependencies生成服務(wù)并運行它以檢查其是否有效。
> ./gradlew clean assemble> java -cp "build/libs/*" HelloWorld ... [Thread-0] INFO org.eclipse.jetty.server.Server - Started @363ms> curl localhost:4567/sayHello Hello World!讓我們使用Graal將其編譯為本地二進(jìn)制文件。 幸運的是,該命令與java命令非常相似:
> native-image -cp "build/libs/*" HelloWorld ... Build on Server(pid: 31197, port: 52737)* [helloworld:31197] classlist: 2,142.65 ms [helloworld:31197] (cap): 2,154.21 ms ... ... [helloworld:31197] write: 443.13 ms [helloworld:31197] [total]: 56,525.52 ms現(xiàn)在,我們應(yīng)該在當(dāng)前目錄中擁有本機(jī)二進(jìn)制文件。 讓我們運行它:
> ./helloworld ... [Thread-2] INFO org.eclipse.jetty.server.Server - Started @2ms> curl localhost:4567/sayHello Hello World!可執(zhí)行文件為14Mb,但看該啟動時間為2ms ,基本上是瞬時的! 在內(nèi)存方面,過多地關(guān)注top并不是明智的選擇,但是很明顯,從運行時刪除JVM具有其優(yōu)勢。 這在部署大量獨立進(jìn)程的微服務(wù)系統(tǒng)中尤其重要。
Kotlin呢?
Kotlin是一種JVM語言,正在Swift發(fā)展并且并非沒有道理。 它結(jié)合了功能樣式和OO功能,無縫的Java互操作性和簡潔的語法,使其成為通用的良好語言,并且是Java的明顯替代。 首先要使用Kotlin構(gòu)建我們的服務(wù),我們將Kotlin庫依賴項添加到Gradle(撰寫本文時版本為v1.3.10)。
dependencies { ...compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.10" }并使用Kotlin編譯器插件。
plugins {id 'org.jetbrains.kotlin.jvm' version '1.3.10' }使用Kotlin,我們荒謬的簡單微服務(wù)變得更加簡單。
import spark.Spark.*fun main(args: Array<String>) {get("/sayHello") { req, res -> "Hello World!" } }生成服務(wù)并運行它以檢查其是否有效。
> ./gradlew clean assemble> java -cp "build/libs/*" HelloWorldKt ... [Thread-0] INFO org.eclipse.jetty.server.Server - Started @363ms> curl localhost:4567/sayHello Hello World!讓我們本地編譯它。 因為它是 Java,所以命令幾乎與Java版本相同(Kotlin編譯器會自動將Kt后綴添加到生成的類中)。
> native-image -cp "build/libs/*" HelloWorldKt Build on Server(pid: 53242, port: 51191) [helloworldkt:53242] classlist: 783.03 ms [helloworldkt:53242] (cap): 2,139.45 ms ... [helloworldkt:53242] write: 591.88 ms [helloworldkt:53242] [total]: 53,074.15 ms并運行它:
> ./helloworldkt ... [Thread-2] INFO org.eclipse.jetty.server.Server - Started @2ms> curl localhost:4567/sayHello Hello World!可執(zhí)行文件的大小和啟動速度幾乎與Java版本相同,這是可以預(yù)期的,因為它實質(zhì)上是相同的代碼。
這是一個基本示例,但Kotlin的實現(xiàn)簡化 , SparkJava的簡化 實現(xiàn) 微服務(wù)和Graal的簡化部署相結(jié)合,是微服務(wù)開發(fā)非常有吸引力的主張。
盡管如此,除了更好的語法外,Kotlin與Java非常相似。 我們還可以使用其他JVM語言,這些語言可能會進(jìn)一步推動Graal。
需要Clojure
使用Clojure構(gòu)建微服務(wù)是一個有趣的想法。 服務(wù)本質(zhì)上是自然的功能,實際上服務(wù)是一種功能,語言的動態(tài)特性可能使其成為某些以數(shù)據(jù)為中心的情況的理想選擇。
而不是使用Gradle,我們將從一個新的Leiningen項目開始:
lein new hello-clojure依賴關(guān)系放在main project.clj文件中,以及我們將用來啟動服務(wù)器的主類的名稱。
:dependencies [[org.clojure/clojure "1.9.0"][com.sparkjava/spark-core "2.7.2"][org.slf4j/slf4j-simple "1.7.13"]]:main hello_clojure.core)Clojure可與Java互操作,但程度不及Kotlin。 為了克服這些差異,我編寫了一些適配器,以允許慣用的clojure代碼使用SparkJava的類。
(ns hello_clojure.core(:gen-class)(:import (spark Spark Response Request Route)))(defn route [handler](reify Route(handle [_ ^Request request ^Response response](handler request response))))(defn get [endpoint routefn](Spark/get endpoint (route routefn)))(我后來發(fā)現(xiàn)了一篇不錯的文章 ,其中提供了使用Clojure和SparkJava的完整服務(wù)。它們的適配器比我的適配器稍好,因此我在后面的文章中結(jié)合了一些想法。)
然后,我們準(zhǔn)備創(chuàng)建從main方法執(zhí)行的控制器,以便可以從命令行輕松調(diào)用它。 還要注意,在上面我們使用gen-class指令來確保在清單中指定了主類:
(defn -main [](get "/sayHello" (fn [req resp] "Hello World!!")))為了簡化服務(wù)的生成,我們可以使用Leiningen構(gòu)建一個自包含的jar。
> lein clean && lein uberjar和以前一樣,我們首先檢查該服務(wù)是否可以正常運行Java:
$ java -cp target/hello-clojure-0.1.0-SNAPSHOT-standalone.jar hello_clojure.core ... [Thread-0] INFO org.eclipse.jetty.server.Server - Started @1033ms> curl localhost:4567/sayHello Hello World!編譯為本地映像就像使用Java和Kotlin的先前示例一樣簡單。
> native-image -cp target/hello-clojure-0.1.0-SNAPSHOT-standalone.jar hello_clojure.core Build on Server(pid: 35646, port: 53994)* [hello_clojure.core:35646] classlist: 2,704.82 ms [hello_clojure.core:35646] (cap): 909.58 ms ... [hello_clojure.core:35646] write: 647.23 ms [hello_clojure.core:35646] [total]: 54,900.61 ms并運行它:
> ./helloworld_clojure ... [Thread-2] INFO org.eclipse.jetty.server.Server - Started @2ms> curl localhost:4567/sayHello Hello World!本地二進(jìn)制文件再次大約為15M,并且啟動時間幾乎是瞬時的。
結(jié)論
將Graal與其他基于JVM的語言結(jié)合使用是一個非常誘人的主張,值得進(jìn)一步研究,但是我確實對生產(chǎn)用途存在一些擔(dān)憂。 主要是如果出現(xiàn)問題,公共領(lǐng)域中幾乎沒有信息可以為您提供幫助,而純Java之外的信息則更少。 另一方面,這些都是開源項目,所以什么都沒有隱藏:)
另一個限制是,許多庫根本無法與Graal一起使用。 這并不是完全消極的,因為它會鼓勵我們回到簡單的編碼實踐中,但是您可能會遇到無法更改的依賴關(guān)系,這可能會引起很大的麻煩。 我認(rèn)為最初的主要缺點是反射驅(qū)動的映射,無論是序列化還是ORM品種。 為了使許多庫和框架與Graal 兼容 ,已經(jīng)做了很多工作,但是還處于初期。
第三,主要是實際的考慮是對本機(jī)映像的編譯非常慢。 即使是這個非常簡單的示例,也幾乎需要花費一分鐘的時間來構(gòu)建。 當(dāng)然,您可以僅將開發(fā)編譯為字節(jié)碼,但是兼容性問題可能會消失。 持續(xù)的構(gòu)建流程和全面的測試將是減輕這種風(fēng)險的一種方法。
顯然,要使它成為一個功能齊全的服務(wù)還有很多工作要做,并且在投入生產(chǎn)使用之前要進(jìn)行適當(dāng)?shù)目紤],但是如果我們選擇繼續(xù)使用簡單的代碼,那么問題將被最小化。
翻譯自: https://www.javacodegeeks.com/2019/01/native-microservices-sparkjava-graal.html
總結(jié)
以上是生活随笔為你收集整理的使用SparkJava和Graal的本机微服务的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 4个月是什么字 4个月应该是什么字
- 下一篇: 功能Java示例 第6部分–用作参数