Vert.x入坑須知(3)

隨着Vert.x進化到3.5.0,本系列也迎來了新篇章。前端

CORS的新變化

對於CORS,搞Web開發(不論你是前端,仍是後端)的同志應該不陌生,尤爲是現在微服務盛行的時代,CORS更是最經常使用的配置項之一。倘若你對此還有一點點的疑問,不用問,你已經落伍了!java

Vert.x很早就支持了CORS,但到了3.5.0,出於安全性上的考慮,它增長了一個限制:git

當allowedOriginPattern爲「*」時,allowCredentials不容許爲「true」。

這個限制其實起源自協議的限制:github

The string "*" cannot be used for a resource that supports credentials.The string " *" cannot be used for a resource that supports credentials.

即:Access-Control-Allow-Origin爲「*」時,Access-Control-Allow-Credentials會被忽略,Credentials信息(通常是Cookie)也不會被user-agent發送。你們能夠參考這篇文章英文原版)瞭解如何安全地設置各個header。數據庫

在3.5.0時,當allowedOriginPattern爲「*」和allowCredentials爲「true」時,Vert.x會產生一個異常,若不處理會致使Verticle不能正常啓動。在以前的版本中,不會出現這種狀況。json

CB for handler

斷路器是個好東東,強烈建議在框架設計上就考慮它,以免粗心的開發者寫的代碼擾亂其餘好代碼。在我寫dgatedfx時,對全部對外暴露的服務也都強加了這個限制。後端

既然CB這麼好,是否是動過將其應用到普通Handler上的念頭?不過因爲Vert.x CB的文檔示例中給出的都是清一色的HTTP請求的例子,要想將CB推而廣之應用到普通的Handler上還得費點氣力。安全

dfx中我寫了一個工具類,對此需求進行了封裝:閉包

public static void withCircuitBreaker(Vertx vertx, CircuitBreaker circuitBreaker
        , Accessible accessible, Map params, Handler<Map> successHandler, Handler<Throwable> failureHandler) {
    circuitBreaker.execute(future ->
            vertx.executeBlocking(f -> {
                        try {
                            f.complete(accessible.invoke(params));
                        } catch (Throwable throwable) {
                            f.fail(throwable);
                        }
                    }
                    , result -> {
                        if (result.succeeded()) {
                            future.complete(result.result());
                        } else {
                            future.fail(result.cause());
                        }
                    })
    ).setHandler(result -> {
        if (result.succeeded()) {
            successHandler.handle((Map) result.result());
        } else {
            logger.error("CB[{}] execution failed, cause: ", circuitBreaker.name(), result.cause());
            failureHandler.handle(result.cause());
        }
    });
}

它的使用很簡單:框架

Utils.withCircuitBreaker(vertx, circuitBreaker, accessible, body.getMap()
               , result -> Utils.fireJsonResponse(response, 200, result)
               , throwable -> Utils.fireSingleMessageResponse(response, 500, throwable.getMessage()));

各位可依葫蘆畫瓢將其改成本身的形式,其中的重點在於以「vertx.executeBlocking」方式來執行Handler的代碼。

測試

在稍微正規的隊伍中,自動化測試應該已是一個標準實踐了,而且Vert.x對於測試也提供了支持

不過,我不喜歡。緣由有幾個:

  • Vert.x Unit是基於JUnit,而我已經有鍾情的測試框架:Spock。相比起JUnit而言,後者簡直能夠說是Java測試領域的戰鬥機。
  • 好的單元測試原本就是要儘可能少的依賴所用框架,注意這一點以後,盡力將類設計得好測試,這樣的結果就是普通的單元測試類編寫。既然有讓人爽的工具用,也就沒有必要去用Vert.x Unit了。
  • 對於集成測試,直接模擬實際的環境,再加上合適的timer,目前看來也還不錯。並且Vert.x Unit中也同樣須要用timer,這樣一來,也就沒有必要專門用它了。

爲了證實我所言非虛,你們能夠去看看dgatedfx的測試代碼。這裏給出兩個例子:

都是基於Spock寫的,各位能夠體驗其酸爽度。

運行時外部配置

在本系列第一篇裏,我就提出了一個鐘意的工程結構組成,但裏面沒有提到「運行時外部配置文件」這一常見的實踐。

通過若干項目的錘鍊以後,目前對於這種運行時的外部配置文件,我基本造成了一個固定套路:Groovy DSL + Groovy ConfigSlurper。它倆簡直是完成這一任務的絕配,比起Vert.x的提供的json配置文件要爽太多。

看看Gradle的build文件,你就能夠知道這種DSL的靈活度能夠到什麼程度,更況且Groovy的語法對於Java開發者極其友好。

或許有人會以爲Groovy不酷,甚至有點鄙夷,言必稱Scala、Clojure、Kotlin、Go或者Rust。對此,哥只想說:做爲開發者,最讓人鄙夷的是交不出活,客戶纔不關心你用什麼語言呢!

配置DSL的例子:dgate配置

dgate是我寫的一個基於vertx的輕量級網關,全部的配置所有經過配置文件來定義,無需數據庫。關於它的詳細介紹,能夠參見其文檔。爲了說明上一節採用Groovy DSL的靈活度,這裏展現幾個dgate的配置例子。

靜態Mock

利用dgate的mock功能,分離開的先後端開發人員能夠並行工做,只需將mock響應配置到dgate的配置文件中就好。

"/summary" {
    expected {
        statusCode = 200
        payload {
            eqLocations = []
            opRateInLast30Days = []
            myOrgs = [
                ["name": "org1", "admin": false]
            ]
        }
    }
}

動態Mock

動態Mock爲那些返回動態結果(如某些狀況下成功,某些狀況下失敗)的URL模擬提供了便利。

"/login" {
    required = ["sub", "password"]
    methods = [HttpMethod.GET, HttpMethod.POST]
    expected {
        statusCode = 200
        payload = {
            JWTAuth jwtAuth = Utils.createAuthProvider(Vertx.vertx())
            JWTTokenGenerator tokenGenerator = new JWTTokenGenerator(jwtAuth)
            [token: tokenGenerator.token(["sub": "……", "name": "……", "role": "normal"], 200)]
        }
    }
}

上例將模擬一個實際的動態JWT,這樣的好處在於,方便前端/移動端開發直接去完成刷新token或從新登陸的過程,而不須要再針對此場景作其餘特殊處理。

請注意,爲了產生動態結果,此處的payload使用的是閉包,而不像前一例用的是Map。此時,閉包的返回值爲Mock響應的結果。

以上的例子已經充分展示了將Groovy DSL做爲運行時外部配置的能力,能夠說是完勝Vert.x自帶的json配置方式。

好啦,本期內容就此結束,請保持關注,期待下期繼續!



本系列其餘文章:
相關文章
相關標籤/搜索