• [译] 第十三天:Dropwizard


    前言

    8年软件开发生涯中我俨然一位Java开发者了。大多数我写的程序,都用Spring框架或者Java EE.最近我在学Python Web开发,其中印象很深的一个是Flask框架。Flask框架是个微框架,使得写REST后端很简单。我今天的30天挑战,决定找一款Java框架替代PythonFlask。一番搜索后,我发现Dropwizard框架可以达到如Flask同样的功效。这篇博客,我们来学习怎样用Dropwizard构建RESTful Java MongoDB程序。

    什么是Dropwizard?

    Dropwizard是一款开源Java框架,用于ops友好开发,高性能RESTful后端开发。它由Yammer开发并提供基于JVM后端的支持。 

    Dropwizar延续最优Java库,嵌入到程序包,包括以下组件:

    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的几点原因:

    1. 快速的项目引导:要是你用过Spring或者Java EE, 就会了解开发者要通过项目引导的痛苦,用Dropwizard,只需要添加一个依赖到pom.xml文件就好了。
    2. 项目Metrics:      Dropwizard支持项目metrics, 它提供很有用的信息如请求/相应时间等,我们只需给出@Timed注解就可获得方法执行时间。
    3. 生产力:每个Dropwizard应用有一个启用Jetty容器的主程序,意味着可以在IDE里直接像主程序一样运行和调试,没有必要再编译或者部署WAR文件。 

    Github仓库

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

    前提准备

    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上,打开命令管理器输入以下命令。

    $ 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.

    第二步:更新pom.xml

    现在更新pom.xml, 加入dropwizard-core maven依赖,再更新Maven项目用Java 1.7版本,更新pom.xml后更新Maven项目(右击>Maven>Update Project)

    <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.Configuration

    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

  • 相关阅读:
    团队项目第二阶段冲刺第六天
    团队项目冲刺第二阶段第五天
    团队项目冲刺第二阶段第四天
    团队项目冲刺第二阶段第三天
    大道至简阅读笔记1
    团队项目冲刺第二阶段第二天
    团队项目第二阶段冲刺第一天
    团队项目冲刺第九天
    团队项目冲刺第八天
    团队项目冲刺第七天
  • 原文地址:https://www.cnblogs.com/endless-on/p/3492884.html
Copyright © 2020-2023  润新知