[譯] 第十三天:Dropwizard - 很棒的Java REST 服務棧

前言

8年軟件開發生涯中我儼然一位Java開發者了。大多數我寫的程序,都用Spring框架或者Java EE.最近我在學Python Web開發,其中印象很深的一個是Flask框架。Flask框架是個微框架,使得寫REST後端很簡單。我今天的30天挑戰,決定找一款Java框架替代PythonFlask。一番搜索後,我發現Dropwizard框架能夠達到如Flask一樣的功效。這篇博客,咱們來學習怎樣用Dropwizard構建RESTful Java MongoDB程序。html

什麼是Dropwizard?

Dropwizard是一款開源Java框架,用於ops友好開發,高性能RESTful後端開發。它由Yammer開發並提供基於JVM後端的支持。 java

Dropwizar延續最優Java庫,嵌入到程序包,包括如下組件:linux

  1. 嵌入Jetty: 每一個程序都打包成jar而不是war,啓用自帶的Jetty容器,沒有WAR文件也沒有外部servlet容器。
  2. JAX-RS:      Jersey(JAX-RS的參考實現)用來寫RESTful Web服務,這樣就沒白費你已懂的JAX-RS知識。
  3. JSON: REST服務使用JSON, Jackson庫用來作全部JSON處理。
  4. Logging: 用Logback和SLF4J完成。
  5. Hibernate      Validator: Dropwizard用Hbernate Vlidator API來驗證聲明。
  6. Metrics:      Dropwizard支持用Metrics庫進行檢測,提供觀察代碼對生產作了什麼的絕佳視覺。 

爲何選擇Dropwizard?

我學習Dropwizard的幾點緣由:git

  1. 快速的項目引導:要是你用過Spring或者Java EE, 就會了解開發者要經過項目引導的痛苦,用Dropwizard,只須要添加一個依賴到pom.xml文件就行了。
  2. 項目Metrics:      Dropwizard支持項目metrics, 它提供頗有用的信息如請求/相應時間等,咱們只需給出@Timed註解就可得到方法執行時間。
  3. 生產力:每一個Dropwizard應用有一個啓用Jetty容器的主程序,意味着能夠在IDE裏直接像主程序同樣運行和調試,沒有必要再編譯或者部署WAR文件。 

Github倉庫

今天的demo放在github: day13-dropwizard-mongodb-demo-appgithub

前提準備

  1. 必須會Java基礎。
  2. 下載和安裝MongoDB數據庫
  3. 安裝最新Java Development      Kit(JDK), 能夠裝OpenJDK7或者Oracle JDK 7. OpenShift支持OpenJDK 6和7, 這篇博客,咱們用JDK 7.
  4. 官網下載最新的Eclipse,目前最新版本是Kepler.

         

安裝Eclipse很簡單,只需解壓下載的安裝包。在linux或者mac上,打開命令管理器輸入如下命令。mongodb

$ tar -xzvf eclipse-jee-kepler-R-*.tar.gz

Windows上能夠用7-zip或者其餘解壓工具解壓,解壓後,在你解壓的路徑會有一個eclipse的文件夾,能夠給可執行文件建立快捷鍵。數據庫

第一步:新建Maven項目

打開Eclipse IDE導航到項目空間,新建項目,到File > New > Maven Project,選擇maven-archetype-quichstart,輸入Ground IdArtifact Id,最後點Finish.apache

第二步:更新pom.xml

如今更新pom.xml, 加入dropwizard-core maven依賴,再更新Maven項目用Java 1.7版本,更新pom.xml後更新Maven項目(右擊>Maven>Update Project)json

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>com.shekhar</groupId>
    <artifactId>blog</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
 
    <name>blog</name>
    <url>http://maven.apache.org</url>
 
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
 
    <dependencies>
        <dependency>
            <groupId>com.yammer.dropwizard</groupId>
            <artifactId>dropwizard-core</artifactId>
            <version>0.6.2</version>
        </dependency>
 
    </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
 
 
        </plugins>
    </build>
</project>
View Code

第三步:建立配置類

每一個Dropwizard程序都有一個配置類,用來指定特定環境變量。後面咱們會添加MongoDB配置參數如host, port, db name.這個類繼承com.yammer.dropwizard.config.Configurationflask

import com.yammer.dropwizard.config.Configuration;
 
public class BlogConfiguration extends Configuration{
 
}
View Code

第四步:建立服務類

Dropwizard由一個服務類引導,這個類涵蓋全部提供基礎功能的綁定和命令,也啓用內嵌的Jetty服務,繼承com.yammer.dropwizard.Service.

import com.yammer.dropwizard.Service;
import com.yammer.dropwizard.config.Bootstrap;
import com.yammer.dropwizard.config.Environment;
 
public class BlogService extends Service<BlogConfiguration> {
 
    public static void main(String[] args) throws Exception {
        new BlogService().run(new String[] { "server" });
    }
 
    @Override
    public void initialize(Bootstrap<BlogConfiguration> bootstrap) {
        bootstrap.setName("blog");
    }
 
    @Override
    public void run(BlogConfiguration configuration, Environment environment) throws Exception {
 
    }
 
}
View Code

 

以上代碼作了如下動做:

  1. 這個類有一個主方法,做爲服務的入口,在這個主方法裏,建立了一個BlogService實例來調用run方法,server命令用參數形式傳遞,它會啓動內嵌的Jetty服務。
  2. Initalize 方法在執行service run方法前調用,設置服務名爲blog.
  3. 接下來,有一個run方法會在service運行時調用,後面,咱們會添加JAX-RS資源到這個方法。

第五步:寫IndexResource

來寫第一個當GET請求 '/' url時會被引用的資源,新建一個JAX-RS資源,它會列出全部博客。

import java.util.Arrays;
import java.util.List;
 
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
 
import com.yammer.metrics.annotation.Timed;
 
@Path("/")
public class IndexResource {
 
    @GET
    @Produces(value = MediaType.APPLICATION_JSON)
    @Timed
    public List<Blog> index() {
        return Arrays.asList(new Blog("Day 12: OpenCV--Face Detection for Java Developers",
                "https://www.openshift.com/blogs/day-12-opencv-face-detection-for-java-developers"));
    }
}
View Code

 

以上代碼是一個標準的JAX-RS資源類,註釋了@Path,定義了index()方法。Index()返回博客的集合,這些博客會被轉換成JSON文檔,@Timed註釋肯定了Dropwizard基礎時間。 

 

以上IndexResource用一篇博客呈現,顯示以下,博客的呈現用hibernate validator註釋來確保內容有效性。例如,用@URL註釋來確保只有有效的URL會存到MongoDB數據庫。

import java.util.Date;
import java.util.UUID;
 
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.URL;
 
public class Blog {
 
    private String id = UUID.randomUUID().toString();
 
    @NotBlank
    private String title;
 
    @URL
    @NotBlank
    private String url;
 
    private final Date publishedOn = new Date();
 
    public Blog() {
    }
 
    public Blog(String title, String url) {
        super();
        this.title = title;
        this.url = url;
    }
 
    public String getId() {
        return id;
    }
 
    public String getTitle() {
        return title;
    }
 
    public String getUrl() {
        return url;
    }
 
    public Date getPublishedOn() {
        return publishedOn;
    }
}
View Code

 

而後,在servicerun方法裏註冊IndexResource, 用如下代碼更新BlogServicerun方法。

@Override
public void run(BlogConfiguration configuration, Environment environment) throws Exception {
   environment.addResource(new IndexResource());
}
View Code

 

如今咱們能夠把BlogService做爲主程序運行,右擊>Run As>Java Application. 會啓動內嵌Jetty容器而後看到程序運行在http://localhost:8080/.

$ curl http://localhost:8080
 
[{"id":"9bb43d53-5436-4dac-abaa-ac530c833df1","title":"Day 12: OpenCV--Face Detection for Java Developers","url":"https://www.openshift.com/blogs/day-12-opencv-face-detection-for-java-developers","publishedOn":1384090975372}]

 

管理界面在http://localhost:8081/ 

 

點擊Metrics能夠查看IndexResourceMetricx, 數據是JSON格式。

"com.shekhar.blog.IndexResource" : {
    "index" : {
      "type" : "timer",
      "duration" : {
        "unit" : "milliseconds",
        "min" : 17.764,
        "max" : 17.764,
        "mean" : 17.764,
        "std_dev" : 0.0,
        "median" : 17.764,
        "p75" : 17.764,
        "p95" : 17.764,
        "p98" : 17.764,
        "p99" : 17.764,
        "p999" : 17.764
      },
      "rate" : {
        "unit" : "seconds",
        "count" : 1,
        "mean" : 7.246537731991882E-4,
        "m1" : 2.290184897291144E-12,
        "m5" : 3.551918562683463E-5,
        "m15" : 2.445031498756583E-4
      }
    }
  },
View Code

第六步:配置MongoDB

pom.xml裏添加Mongo-jackson-mapper依賴。

<dependency>
    <groupId>net.vz.mongodb.jackson</groupId>
    <artifactId>mongo-jackson-mapper</artifactId>
    <version>1.4.2</version>
</dependency>
View Code

 

更新BlogConfiguration類的MongoDB數據庫信息,如host, port, database name.

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
 
import org.codehaus.jackson.annotate.JsonProperty;
import org.hibernate.validator.constraints.NotEmpty;
 
import com.yammer.dropwizard.config.Configuration;
 
public class BlogConfiguration extends Configuration {
 
    @JsonProperty
    @NotEmpty
    public String mongohost = "localhost";
 
    @JsonProperty
    @Min(1)
    @Max(65535)
    public int mongoport = 27017;
 
    @JsonProperty
    @NotEmpty
    public String mongodb = "mydb";
}
View Code

 

接下來新建一個MongoManaged類,讓咱們能控制程序啓動和中止,它實現接口com.yammer.dropwizard.lifecycle.Managed.

import com.mongodb.Mongo;
import com.yammer.dropwizard.lifecycle.Managed;
 
public class MongoManaged implements Managed {
 
    private Mongo mongo;
 
    public MongoManaged(Mongo mongo) {
        this.mongo = mongo;
    }
 
    @Override
    public void start() throws Exception {
    }
 
    @Override
    public void stop() throws Exception {
        mongo.close();
    }
 

}
View Code

以上代碼,咱們在Stop方法裏關掉MongoDB鏈接。

 

接下來咱們建立一個MongoHealthCheck來檢查MongoDB是鏈接仍是斷開的,Health checkDropwizard在生產環境作執行時測試檢測服務的行爲的功能。

import com.mongodb.Mongo;
import com.yammer.metrics.core.HealthCheck;
 
public class MongoHealthCheck extends HealthCheck {
 
    private Mongo mongo;
 
    protected MongoHealthCheck(Mongo mongo) {
        super("MongoDBHealthCheck");
        this.mongo = mongo;
    }
 
    @Override
    protected Result check() throws Exception {
        mongo.getDatabaseNames();
        return Result.healthy();
    }
 
}
View Code

 

如今更新BlogService, 加入MongoDB配置。

package com.shekhar.blog;
 
import com.mongodb.Mongo;
import com.yammer.dropwizard.Service;
import com.yammer.dropwizard.config.Bootstrap;
import com.yammer.dropwizard.config.Environment;
 
public class BlogService extends Service<BlogConfiguration> {
 
    public static void main(String[] args) throws Exception {
        new BlogService().run(new String[] { "server" });
    }
 
    @Override
    public void initialize(Bootstrap<BlogConfiguration> bootstrap) {
        bootstrap.setName("blog");
    }
 
    @Override
    public void run(BlogConfiguration configuration, Environment environment) throws Exception {
        Mongo mongo = new Mongo(configuration.mongohost, configuration.mongoport);
        MongoManaged mongoManaged = new MongoManaged(mongo);
        environment.manage(mongoManaged);
 
        environment.addHealthCheck(new MongoHealthCheck(mongo));
 
        environment.addResource(new IndexResource());
    }
 
}
View Code

以上代碼:

  1. BlogConfiguration對象新建了一個Mongo實例。
  2. 新建了MongoManaged實例並添加到環境中。
  3. 添加了正常檢測。

 

以主程序運行應用,能夠經過http://localhost:8081/healthcheckHealthCheck頁面檢測MongoDB是否在運行,若是沒有運行,能夠看到異常信息。

! MongoDBHealthCheck: ERROR
!  can't call something : Shekhars-MacBook-Pro.local/192.168.1.101:27017/admin
 
com.mongodb.MongoException$Network: can't call something : Shekhars-MacBook-Pro.local/192.168.1.101:27017/admin
    at com.mongodb.DBTCPConnector.call(DBTCPConnector.java:227)
    at com.mongodb.DBApiLayer$MyCollection.__find(DBApiLayer.java:305)
    at com.mongodb.DB.command(DB.java:160)
    at com.mongodb.DB.command(DB.java:183)
    at com.mongodb.Mongo.getDatabaseNames(Mongo.java:327)
    at com.shekhar.blog.MongoHealthCheck.check(MongoHealthCheck.java:17)
    at com.yammer.metrics.core.HealthCheck.execute(HealthCheck.java:195)
    at 
Caused by: java.io.IOException: couldn't connect to [Shekhars-MacBook-Pro.local/192.168.1.101:27017] bc:java.net.ConnectException: Connection refused
    at com.mongodb.DBPort._open(DBPort.java:228)
    at com.mongodb.DBPort.go(DBPort.java:112)
    at com.mongodb.DBPort.call(DBPort.java:79)
    at com.mongodb.DBTCPConnector.call(DBTCPConnector.java:218)
    ... 33 more
 
* deadlocks: OK

 

啓動MongoDB能夠看到以下

* MongoDBHealthCheck: OK
* deadlocks: OK

第七步:建立BlogResource

如今來寫BlogResource類,用於響應建立博客的入口。

import java.util.ArrayList;
import java.util.List;
 
import javax.validation.Valid;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
 
import net.vz.mongodb.jackson.DBCursor;
import net.vz.mongodb.jackson.JacksonDBCollection;
 
import com.yammer.metrics.annotation.Timed;
 
@Path("/blogs")
@Produces(value = MediaType.APPLICATION_JSON)
@Consumes(value = MediaType.APPLICATION_JSON)
public class BlogResource {
 
    private JacksonDBCollection<Blog, String> collection;
 
    public BlogResource(JacksonDBCollection<Blog, String> blogs) {
        this.collection = blogs;
    }
 
    @POST
    @Timed
    public Response publishNewBlog(@Valid Blog blog) {
        collection.insert(blog);
        return Response.noContent().build();
    }
}
View Code

 

Java程序類型運行BlogService類,要測試BlogResource, 設置一個curl請求。

$ curl -i -X POST -H "Content-Type: application/json" -d '{"title":"Day 12: OpenCV--Face Detection for Java Developers","url":"https://www.openshift.com/blogs/day-12-opencv-face-detection-for-java-developers"}' http://localhost:8080/blogs
 
 
HTTP/1.1 204 No Content
Date: Sun, 10 Nov 2013 14:08:03 GMT
Content-Type: application/json

第八步:更新IndexResource

如今更新IndexResource index()方法,從MongoDB獲取全部博客文檔。

import java.util.ArrayList;
import java.util.List;
 
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
 
import net.vz.mongodb.jackson.DBCursor;
import net.vz.mongodb.jackson.JacksonDBCollection;
 
import com.yammer.metrics.annotation.Timed;
 
@Path("/")
public class IndexResource {
 
    private JacksonDBCollection<Blog, String> collection;
 
    public IndexResource(JacksonDBCollection<Blog, String> blogs) {
        this.collection = blogs;
    }
 
    @GET
    @Produces(value = MediaType.APPLICATION_JSON)
    @Timed
    public List<Blog> index() {
        DBCursor<Blog> dbCursor = collection.find();
        List<Blog> blogs = new ArrayList<>();
        while (dbCursor.hasNext()) {
            Blog blog = dbCursor.next();
            blogs.add(blog);
        }
        return blogs;
    }
 
}
View Code

 

更新BlogService run方法,傳遞博客集合到IndexResource.

@Override
    public void run(BlogConfiguration configuration, Environment environment) throws Exception {
        Mongo mongo = new Mongo(configuration.mongohost, configuration.mongoport);
        MongoManaged mongoManaged = new MongoManaged(mongo);
        environment.manage(mongoManaged);
 
        environment.addHealthCheck(new MongoHealthCheck(mongo));
 
        DB db = mongo.getDB(configuration.mongodb);
        JacksonDBCollection<Blog, String> blogs = JacksonDBCollection.wrap(db.getCollection("blogs"), Blog.class, String.class);
 
        environment.addResource(new IndexResource(blogs));
 
        environment.addResource(new BlogResource(blogs));
    }
View Code

 

Java程序形式運行BlogService, 要測試BlogResource, 設置curl請求。

$ curl http://localhost:8080
 
[{"id":"527f9806300462bbd300687e","title":"Day 12: OpenCV--Face Detection for Java Developers","url":"https://www.openshift.com/blogs/day-12-opencv-face-detection-for-java-developers","publishedOn":1384093702592}]

第九步:發佈到雲

這有一個博客講咱們怎樣發佈Dropwizard程序到OpenShift, 參考博客 

 

這是今天的內容,繼續給反饋吧。 

 

原文:https://www.openshift.com/blogs/day-13-dropwizard-the-awesome-java-rest-server-stack

相關文章
相關標籤/搜索