天天看點

Java微服務開發指南 -- 使用Dropwizard建構微服務使用Dropwizard建構微服務

    Dropwizard的曆史要早于Spring Boot和WildFly Swarm,它最早是在<code>2011.12</code>釋出的<code>v0.1.0</code>版本,在本文編寫的過程中,它已經釋出了<code>v0.9.2</code>版本,而<code>v1.0.0</code>版本也在準備中了。Dropwizard是<code>Coda Hale</code>在<code>Yammer</code>公司時創立的,它旨在提升公司分布式系統的架構(現在叫:微服務)。雖然它最早被用來建構REST Web 服務,而現在它具備了越來越多的功能,但是它的目标始終是作為輕量化、為生産環境準備且容易使用的web架構。

目前Dropwizard已經釋出了<code>v1.1.0</code>版本

    Dropwizard與Spring Boot類似,也是建構微服務可選的工具,但是它顯得比Spring Boot更加規範一些。它使用的元件一般不會做可選替換,而好處是可以不需要那麼多的修飾,比如寫基于REST的web服務。比方說,Dropwizard選擇使用<code>Jetty</code>作為Servlet容器,REST庫使用<code>Jersey</code>,序列化和反序列化使用了<code>Jackson</code>,而想将其中的<code>Jetty</code>替換成<code>Undertow</code>就沒有那麼容易。

    Dropwizard預設也不具備依賴注入的容器(像Spring或者CDI),你當然可以自行添加,但是Dropwizard推薦你把微服務弄的簡單一些,不需要這些額外的元件。Spring Boot 隐藏的非常多的底層實作,而這些内容十分的複雜,就像Spring隐藏了通過注解可以完成Bean注入這個複雜的場景一樣。雖然注解很好用,也解決了某些領域比較瑣碎的代碼,但是當你想在生産環境DEBUG或者排查問題時,這些東西往往會把簡單的問題搞得很複雜。Dropwizard推薦所有的内容都顯示的使用,你得到的輸出也就更加的肯定和明确。

原文涉及到生産環境的DEBUG,筆者認為更多的是問題排查

    就像Spring Boot一樣,Dropwizard推薦将整個工程打包成一個可執行的jar,通過這種方式開發人員不用在擔心程式運作的應用伺服器是什麼,需要什麼額外的配置,應用再也不需要被建構成war包了,而且也不會有那麼多複雜層級的類加載器了。Dropwizard中的類加載也是扁平結構的,它和我們常用的應用伺服器不一樣,應用伺服器往往具備多層級如同圖一般的類加載器,這會涉及到類加載器的優先級,而這些在不同的應用伺服器的實作上都是大相徑庭的。運作在獨立程序中的Dropwizard執行個體也便于進行各自的JVM調優和監控,因為運作在應用伺服器上的多個應用,很可能由于一個應用導緻的GC或者記憶體溢出,進而導緻整個應用伺服器的崩潰,畢竟它們是在同一個程序中。

    Dropwizard在優秀的三方庫協助下,提供了不錯的抽象層,使之更有效率,更簡單的編寫生産用途的微服務。

Servlet容器使用<code>Jetty</code>

REST/JAX-RS實作使用<code>Jersey</code>

JSON序列化使用<code>Jackson</code>

內建<code>Hibernate Validator</code>

Guava

Metrics

SLF4J + Logback

資料通路層上使用<code>JDBI</code>

    Dropwizard偏執的認為架構就是用來寫代碼的,是以對于架構的底層技術棧的調整,原則上Dropwizard是拒絕的。正因為它這麼做,使得Dropwizard開發起代碼來更快,而且配置更加容易。<code>Jetty</code>、<code>Jersey</code>和<code>Jackson</code>都是廣為人知的項目,使用它們來構造用于生産環境的web服務,看起來沒什麼毛病,而google的<code>Guava</code>作為提供了工具類的包顯然經得住考驗,而Dropwizard Metrics更是一個強大工具,它能夠暴露出應用相當多的運作細節,而正因為此,Dropwizard Metrics被廣泛的使用于Spring Boot和WildFly Swarm。

    Dropwizard暴露了如下抽象,如果你能掌握這些簡單的抽象,你就能很快的使用Dropwizard進行開發了。

Application

包含了<code>public void main()</code>方法

Environment

放置<code>servlet</code>、<code>resources</code>、<code>filters</code>、<code>health checks</code>、<code>task</code>的地方

Configuration

用于改變環境或者系統配置的地方

Commands

當我們啟動微服務後,使用它來與微服務互動

Resources

REST/JAX-RS資源

Tasks

對于應用的管理,比如改變日志級别或者暫停資料庫連接配接等

    當你啟動一個Dropwizard應用,一個<code>Jetty</code>服務就會啟動,同時它會建立兩個Handler:一個在8080,為你的應用提供服務,另一個在8081,這個提供管理功能。Dropwizard之是以這麼做,是因為不想将管理功能通過8080進行暴露,這樣你可以把端口隐藏在防火牆後面。諸如Metrics和健康檢查,這些也是暴露在管理端口上的,區分的很大原因是考慮安全問題。

    Dropwizard沒有那些美輪美奂用于建立工程的工具,它隻有一個最簡單的方式:maven archetype,或者在已經搭建好的項目中,增加一些maven配置。當然你可以使用<code>jboss-forge</code>來完成工程的建立,用它來添加對應的依賴等等,但是本章,我們還是使用maven archetype。

    選擇一個目錄,然後輸入一段maven指令,可以完成項目的建立。

本示例示範,在<code>microservices-camp</code>目錄下運作

    該指令會在<code>microservices-camp</code>目錄下建立一個名為<code>hola-dropwizard</code>的工程,你可以選擇将其導入到自己的IDE中,或者你可以在<code>hola-dropwizard</code>目錄下運作<code>mvn clean install</code>完成建構。

    導入<code>hola-dropwizard</code>工程後,你可以看到如下結構:

    Dropwizard已經建立了你需要開發放置的包,它推薦你按照約定進行開發。

api

放置REST資源需要使用的POJOs,你可以了解為domain objects或者DTOs

cli

放置你需要添加給應用的Dropwizard指令

client

用戶端工具類放在這裡

db

和資料庫相關的代碼

health

運作時刻暴露在管理端口的微服務健康檢查邏輯

resources

REST資源

    同樣我們還可以看到一家建立好的類型<code>HolaDropwizardApplication</code>和<code>HolaDropwizardConfiguration</code>,它們使用來啟動和配置應用的。先看一下<code>HolaDropwizardApplication</code>長得樣子。

    這個類包含了一個<code>public static void main()</code>方法,可以想象,它是入口,而<code>getName()</code>方法将會在應用啟動時展示。<code>initialize()</code>和<code>run()</code>方法是用來啟動應用的地方。

    配置類型已經建立了,但是目前是空的。

    雖然Dropwizard沒有定義自己的maven plugin,但是它為我們生成了<code>pom.xml</code>。打開<code>pom.xml</code>,可以看到Dropwizard使用<code>maven-shade-plugin</code>将依賴打包成一個jar,這意味着工程依賴的jar包和代碼将全部解壓,然後重新組合成一個jar。而針對這個建構好的jar,我們可以使用<code>maven-jar-plugin</code>運作它。

    我們唯一需要的一個插件就是<code>exec-maven-plugin</code>,這樣我們就可以像使用<code>mvn spring-boot:run</code>一樣運作它。

    接下來,可以在<code>hola-dropwizard</code>使用<code>mvn exec:java</code>來啟動它。看到如下内容,代表啟動成功了。

    你可以打開浏覽器,通路預設的REST端點:<code>http://localhost:8080</code>,這會不會傳回很多内容,你可能看到:

    如果你通路管理端口:<code>http://localhost:8081</code>,你會看到一個簡單頁面以及連結,這裡面是目前應用的運作時資訊。

    現在使用Dropwizard建構的工程已經準備好了,讓我們添加一個REST端點。我們會在<code>/api/holaV1</code>暴露一個HTTP/REST端點,通路它,将會傳回 Hola Dropwizard @ X,而 X 指的是運作應用的機器IP。如果想做到這個,首先需要在<code>resources</code>包下面建立類型,比如:<code>HolaRestResourceV1</code>(記住,類型放置的包,必須符合Dropwizard的約定)。添加一個方法<code>hola()</code>,然後在其中傳回所需的内容。

    可以針對這個<code>hola()</code>方法做測試。

    這看起來很像Spring Boot,我們想建立REST端點以及服務,也是在POJO上增加一些JAX-RS的注解。

    現在,打開之間生成的<code>HolaDropwizardApplication</code>,在<code>run()</code>方法中添加新建立的<code>HolaRestResourceV1</code>(REST資源)。

    接着就可以在<code>hola-dropwizard</code>目錄下執行<code>mvn clean package exec:java</code>,随着應用的啟動,我們打開浏覽器通路<code>http://localhost:8080/api/holaV1</code>,可以看到如下内容。

    Dropwizard提供了針對内置元件的諸多配置(比如:servlet引擎或者資料源)方式,你可以使用配置檔案來完成配置。可以使用系統環境變量或者Java System properties來進行配置,這樣可以使應用運作在不同的環境上。如同之前介紹的Spring Boot,Dropwizard也可以将配置綁定到指定的執行個體上。在接下來的例子中,我們就将<code>helloapp.*</code>下面的配置,綁定到<code>HolaRestResourceV2</code>上。不像Spring Boot通過<code>application.properties</code>完成配置,Dropwizard隻支援<code>YAML</code>。

    在工程的根目錄(<code>hola-dropwizard</code>)下(注意:不是類路徑下),建立一個<code>conf/application.yml</code>(如果conf目錄不存在,你需要建立它),我們将配置檔案放置在該目錄中,先給<code>conf/application.yml</code>添加一些内容:

    這樣我們為屬性指定了值,如果我們需要為某些環境更改這個值,該如何做呢?可以通過Java System properties來做到,可以通過定義<code>-Ddw.helloapp.saying=Guten Tag</code>。注意<code>dw.*</code>代表着Dropwizard可以覆寫該配置的值,但是如果需要使用作業系統變量來進行覆寫呢,如何做到?

    可以看到對<code>saying</code>的配置首先回去檢視環境變量<code>HELLOAPP_SAYING</code>,如果該環境變量不存在,那麼就會使用預設的<code>Guten Tag aus</code>,預設Dropwizard不會從環境變量中擷取配置,如果需要讓Dropwizard使用環境變量,需要做一些額外改動。打開<code>HolaDropwizardApplication</code>,編輯<code>initialize()</code>方法。

    接下來建立配置,我們定義了一個專門的配置類型,這個配置類型用于接受來自<code>helloapp</code>下的配置,接下來看如何将配置類型和<code>application.yml</code>綁定。在<code>resources</code>包下,建立一個類型。

    這個簡單的Java Bean上增加了一些注解,比如:<code>Jackson</code>和<code>Bean Validator</code>,這個配置對象将會包裝在<code>application.yml</code>中,處于<code>helloapp</code>之下的配置。這一刻,隻有一個配置屬性<code>saying</code>,下面需要将<code>HolaDropwizardConfiguration</code>與它關聯起來。

筆者注:HolaDropwizardConfiguration代表着指定的<code>application.yml</code>,而<code>helloapp</code>是配置中的一個節點,而該節點以下的結構,将會設定到類型<code>HelloSayingFactory</code>執行個體中

    接下來需要将配置引入到REST資源中,在<code>resources</code>包下,建立類型<code>HolaRestResourceV2</code>。

    由于Dropwizard沒有依賴注入架構的幫助,是以你需要依靠自己将配置注入到REST資源中。編輯<code>HolaDropwizardApplication</code>:

    到目前為止,一個具備配置注入的項目已經基本搭建完成,這個例子中對于配置,我們故意設計的複雜一些,目的是還原一個真實場景。雖然開起來零碎的步驟不少,但是可以看到最關鍵的幾個步驟:定義<code>application.yml</code>,并且将<code>HolaDropwizardConfiguration</code>與之綁定,而後續配置的添加就變得很簡單了。

    如果想在maven中運作,還需要将配置檔案的位置傳遞給Dropwizard,是以還需要編輯一下<code>pom.xml</code>

    在項目目錄<code>hola-dropwizard</code>下,執行<code>mvn clean package exec:java</code>,然後打開浏覽器通路<code>http://localhost:8080/api/holaV2</code>,可以看到:

Java微服務開發指南 -- 使用Dropwizard建構微服務使用Dropwizard建構微服務

    接下來停止應用,然後導出一個環境變量,再啟動它,随後通路原來的頁面:

Java微服務開發指南 -- 使用Dropwizard建構微服務使用Dropwizard建構微服務

    Dropwizard中做的最好的是将Metrics作為了一等公民,Dropwizard從一開始就考慮了Metrics,而非像其他架構一樣事後考慮,是以當一個Dropwizard應用啟動的同時,<code>8081</code>管理端口就暴露了Metrics資訊。我們隻需要在對應的REST資源上增加一些注解就可以做到。

    在<code>HolaRestResourceV2</code>中的<code>hola()</code>方法,增加注解<code>@Timed</code>,它将跟蹤該服務的調用耗時與次數等資訊,當然還有其他的Metrics元件可供選擇。

@Metered

服務調用頻率

@ExceptionMetered

異常抛出頻率

不能都添加上,隻能選擇一種

    重新啟動Dropwizard應用,然後通路幾次<code>http://localhost:8080/api/holaV2</code>,然後用浏覽器打開<code>http://localhost:8081/metrics?pretty=true</code>,然後搜尋<code>hola</code>,你可以看到類似如下内容:

    Dropwizard通過<code>maven-shade-plugin</code>打包成了一個jar,是以隻需要通過<code>java -jar</code>就可以運作,我們唯一需要知道的就是傳遞配置給Dropwizard,比如這樣:<code>java -jar target/hola-dropwizard-1.0.jar server conf/application.yml</code>。

    在微服務環境下,服務之間需要互相調用,和之前的Spring Boot應用一樣,Dropwizard提供了自己的REST用戶端供我們使用。類似之前在Spring Boot的章節,我們使用Dropwizard完成這項工作。

Java微服務開發指南 -- 使用Dropwizard建構微服務使用Dropwizard建構微服務

在開始之前,先在<code>hola-dropwizard</code>項目中添加依賴。

    首先建立<code>GreeterSayingFactory</code>配置,在這個配置中将描述調用<code>hola-backend</code>的具體URL和端口等資訊。

    然後需要将<code>application.yml</code>中的配置設定到<code>GreeterSayingFactory</code>中,是以需要将<code>greeter</code>下面的配置完成設定,這時,需要繼續編輯<code>HolaDropwizardConfiguration</code>,增加以下兩個方法。

    對于<code>GreeterSayingFactory</code>中屬性的具體配置,需要在<code>conf/application.yml</code>中編輯,增加以下内容:

    通過這樣就可以使用系統環境變量對應用做不同的配置了,到此配置基本結束,我們編寫一個REST端點用于提供HTTP服務。

    可以看到,通過通路:<code>http://localhost:8080/api/greeting/1</code>,能夠進行Book資源的查詢工作,但是可以看到<code>GreeterRestResource</code>的構造函數,需要一個<code>javax.ws.rs.client.Client</code>。前面提到,Dropwizard沒有依賴注入的幫助,一切都要靠自己來完成組裝,是以最後還需要編輯<code>HolaDropwizardApplication</code>,在<code>run()</code>方法中,增加以下内容:

    Dropwizard提供了兩種REST調用方式:HttpComponents和Jersey/JAX-RS,預設使用的後者,我們就使用它來完成調用。接下來我們在開發機上部署了<code>hola-backend</code>,它的ip是<code>11.239.175.192</code>,你的環境也許是其他。我們需要将ip設定到環境變量<code>GREETER_BACKEND_HOST</code>上。在<code>hola-dropwizard</code>工程目錄下執行:

    通路<code>http://localhost:8080/api/greeting/1</code>,可以看到如下内容:

Java微服務開發指南 -- 使用Dropwizard建構微服務使用Dropwizard建構微服務

    本章介紹了Dropwizard的基本知識,可以看到一個不同于Spring Boot的方式去暴露REST端點、以及不同的方式進行應用配置,如果你想深入了解Dropwizard可以通路如下内容。

<a href="http://www.dropwizard.io/1.1.0/docs/manual/core.html">Dropwizard Core</a>

<a href="http://www.dropwizard.io/1.1.0/docs/getting-started.html">Dropwizard Getting Started</a>

<a href="http://www.oracle.com/splash/java.net/maintenance/index.html">Client API</a>

<a href="https://github.com/dropwizard/dropwizard">Dropwizard on GitHub</a>

<a href="https://github.com/dropwizard/dropwizard/tree/master/dropwizard-example">Dropwizard examples on GitHub</a>