35 views

如果您搜索「最佳網路框架 」,您可能會偶然發現Techempower基準測試,其中排名超過300個框架,在那裡你可能已經注意到Vert.x是排名最高的。

Vert.x是一個多語言 Web 框架,它支持Java ,Kotlin,Scala,Ruby和Javascript支持的語言之間的共同功能。無論語言如何,Vert.x都在Java虛擬機(JVM)上運行。模塊化和輕量級,它面向微服務開發。

Techempower基準測試衡量從資料庫更新,獲取和交付數據的性能。每秒提供的請求越多越好。在這種涉及很少計算的IO場景中,任何非阻塞框架都會有優勢。近年來,這種範式幾乎與Node.js不可分割,Node.js通過其單線程事件循環來推廣它。

與Node類似,Vert.x運行單個事件循環。但Vert.x也利用了JVM。Node運行在單個核心上,而Vert.x維護的線程池大小可以與可用核心數相匹配。憑藉更強的併發支持,Vert.x不僅適用於IO,也適用於需要并行計算的CPU繁重流程。

然而,事件循環只是故事的一半。另一半與Vert.x幾乎沒有關係。Java必備的 15 個框架,推薦看下。

要連接到資料庫,客戶端需要連接器驅動程序。在Java領域,Sql最常見的驅動程序是JDBC。問題是,這個驅動程序阻塞了。它在套接字級別阻塞。一個線程總會卡在那裡,直到它返回一個響應。

毋庸置疑,驅動程序一直是實現完全無阻塞應用程序的瓶頸。幸運的是,在具有多個活動分叉的非同步驅動程序上取得了進展(儘管是非官方的),其中包括:

  • https://github.com/jasync-sql/jasync-sql(適用於Postgres和MySql)
  • https://github.com/reactiverse/reactive-pg-client(Postgres)

黃金法則

使用Vert.x非常簡單,只需幾行代碼即可啟動http伺服器。

val vertx = Vertx.vertx()
vertx.createHttpServer().requestHandler(req => {

}).listen(8080)

方法requestHandler是事件循環傳遞請求事件的地方。由於Vert.x沒有意見,處理它是自由的風格。但請記住非阻塞線程的唯一重要規則:不要阻止它。

在使用併發時,我們可以從如今的許多選項中獲取,例如Promise,Future,Rx,以及Vert.x自己的慣用方法。但隨著應用程序複雜性的增加,單獨使用非同步功能是不夠的。我們還需要輕鬆協調和鏈接調用,同時避免回調地獄,以及優雅地傳遞任何錯誤。

Scala Future滿足上述所有條件,並具有基於函數式編程原理的額外優勢。雖然本文不深入探討Scala Future,但我們可以通過一個簡單的應用程序來嘗試它。

假設該應用程序是一個API服務,用於查找給定其ID的用戶:

val vertx = Vertx.vertx()
vertx.createHttpServer().requestHandler(req => {

req.path() match {
  case p if p contains("/user") =>
  val f = for {
    f1 <- Future { req.getParam("id").get.toInt }
    f2 <- if (f1 < 100) Future.unit else Future.failed(CustomException())
    f3 <- Future { getUserFromDb(f1) }
  } yield f3
  f map (r => printout(req, r)) recover {case exception => printout(req, handleException(exception))}

  case _ => printout(req, "Default page")
}

})
.listen(8080)

def printout(req: HttpServerRequest, msg: String) = req.response().end(msg)

def handleException(e: Throwable): String = {
e match {
  case t: NoSuchElementException => "Missing parameter"
  case t: NumberFormatException => "Parameter not number"
  case t: CustomException => "Custom exception"
  case t: SQLException => "Database error"
  case _ => "Unknown error"
}
}

def getUserFromDb(id: Int) = "mock user name"

case class CustomException() extends Exception("custom exception")

涉及三個操作:檢查請求參數,檢查id是否有效以及獲取數據。我們將把這些操作包裝在Future中,並在「for comprehension」結構中協調執行。

第一步是將請求與服務匹配。

Scala具有強大的模式匹配功能,我們可以將其用於此目的。在這裡,我們攔截任何提及「/ user」並將其傳遞給我們的服務。

接下來是這項服務的核心,我們的期貨按順序排列。

第一個furture 未來f1包裝參數檢查。我們特別想從get請求中檢索id並將其轉換為int。(如果返回值是方法中的最後一行,Scala不需要顯式返回。)如您所見,此操作可能會拋出異常,因為id可能不是int或甚至不可用,但現在可以。

第二個furture f2檢查id的有效性。

我們通過使用我們自己的CustomException顯式調用Future.failed來阻止任何低於100的id。否則,我們以Future.unit的形式傳遞一個空的Future作為成功驗證。

最後的furture f3將使用f1提供的id檢索用戶。

由於這只是一個示例,我們並沒有真正連接到資料庫。我們只返回一些模擬字元串。

map運行從f3生成用戶數據的排列,然後將其列印到響應中。

現在,如果在序列的任何部分發生錯誤,則傳遞Throwable進行恢復。

在這裡,我們可以將其類型與合適的恢復策略相匹配。回顧一下我們的代碼,我們已經預料到了幾個潛在的失敗,例如缺少id,或者id不是int或者無效會導致特定異常。我們通過向客戶端傳遞錯誤消息來處理handleException中的每一個。

這種安排不僅提供從開始到結束的非同步流程,還提供處理錯誤的乾淨方法。由於它是跨處理程序的簡化,我們可以專註於重要的事情,如資料庫查詢。

Verticles,Event Bus和其他陷阱

Vert.x還提供了一個名為verticle的併發模型,類似於Actor系統。Verticle隔離其狀態和行為以提供線程安全的環境。與之通信的唯一方法是通過事件匯流排。

但是,Vert.x事件匯流排要求其消息為String或JSON。

這使得傳遞任意非POJO對象變得困難。在高性能系統中,處理JSON轉換是不可取的,因為它會帶來一些計算成本。如果您正在開發IO應用程序,最好不要使用Verticle或事件匯流排 ,因為這樣的應用程序幾乎不需要本地狀態。

使用某些Vert.x組件也非常具有挑戰性。

您可能會發現缺少文檔,意外行為甚至無法正常運行。Vert.x可能正在遭受其雄心壯志,因為開發新組件需要移植多種語言。這是一項艱巨的任務。因此,堅持核心將是最好的。

如果您正在開發公共API,那麼vertx-core就足夠了。如果它是一個Web應用程序,您可以添加vertx-web,它提供http參數處理和JWT / Session身份驗證。

無論如何,這兩個是主導基準的。在使用vertx-web的一些測試中,性能有所下降,但由於它似乎源於優化,因此可能會在後續版本中得到解決。



Go Top