Vert.x的介紹!這是目前最快的Java框架

banq發表於2019-03-08

如果您最近使用Google搜尋“最佳網路框架”,您可能會偶然發現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領域,Sql最常見的驅動程式是JDBC。問題是,這個驅動程式阻塞了。它在套接字級別阻塞。一個執行緒總會卡在那裡,直到它返回一個響應。
毋庸置疑,驅動程式一直是實現完全無阻塞應用程式的瓶頸。幸運的是,在具有多個活動分叉非同步驅動程式上取得了進展(儘管是非官方的),其中包括:


黃金法則

使用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的一些測試中,效能有所下降,但由於它似乎源於最佳化,因此可能會在後續版本中得到解決。
 

相關文章