REST微服務架構之Dropwizard

1、簡介

DropWizard是由Yammer開發團隊貢獻的一個後臺服務開發框架,其集成了Java生態系統中各個問題域中最優秀的組件,幫助開發者快速的打造一個Rest風格的後臺服務。
   
對開發者來講,使用DropWizard有以下好處:
一、和Maven集成良好,也就是說和Gradle集成也很良好;
二、開發迅速,部署簡單;
三、代碼結構好,可讀性高;
四、自動爲服務提供OM框架;
五、讓開發者天然的把一個應用拆分爲一個個的小服務java

DropWizard結構的Web服務組成
一、Configuration:用於設置該服務的配置,比方說在服務開放在哪一個端口,數據庫配置是怎樣的等等。
二、Application(即Service):該服務的主入口,定義該服務使用哪一個配置文件,開放哪些Resource,該服務須要哪些HealthCheck等等。
三、Resource:定義一個資源,包括如何獲取該資源,對該資源作Get/Post/Delete/Query時,對應的各類業務邏輯。
四、Representation:定義了一個服務返回值對象,當服務返回該對象時,會自動的把該對象按屬性值生成一個Json格式的字符串返回給服務調用者。
五、HealthCheck:在DropWizard爲每一個服務提供的OM框架中用到,經過它能夠隨時檢測當前服務是否可用。數據庫

2、配置環境

1. eclipse新建maven項目apache

2. 配置pom.xml,加入dropwizard須要的依賴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.wbf</groupId>
	<artifactId>dropwizardTest</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>dropwizard test</name>

	<properties>
		<dropwizard.version>0.9.2</dropwizard.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>io.dropwizard</groupId>
			<artifactId>dropwizard-core</artifactId>
			<version>${dropwizard.version}</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-shade-plugin</artifactId>
				<version>1.6</version>
				<configuration>
					<createDependencyReducedPom>true</createDependencyReducedPom>
					<filters>
						<filter>
							<artifact>*:*</artifact>
							<excludes>
								<exclude>META-INF/*.SF</exclude>
								<exclude>META-INF/*.DSA</exclude>
								<exclude>META-INF/*.RSA</exclude>
							</excludes>
						</filter>
					</filters>
				</configuration>
				<executions>
					<execution>
						<phase>package</phase>
						<goals>
							<goal>shade</goal>
						</goals>
						<configuration>
							<transformers>
								<transformer
									implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
								<transformer
									implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
									<mainClass>com.example.helloworld.HelloWorldApplication</mainClass>
								</transformer>
							</transformers>
						</configuration>
					</execution>
				</executions>
			</plugin>

			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-jar-plugin</artifactId>
				<version>2.4</version>
				<configuration>
					<archive>
						<manifest>
							<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
						</manifest>
					</archive>
				</configuration>
			</plugin>

		</plugins>
	</build>

</project>

3. 加入dropwizard的環境配置文件:hello-world.yml,將其放到項目的根目錄下bootstrap

template: Hello, %s!
defaultName: Stranger

3、示例代碼

 1. Configuration瀏覽器

package com.example.helloworld;

import org.hibernate.validator.constraints.NotEmpty;

import com.fasterxml.jackson.annotation.JsonProperty;

import io.dropwizard.Configuration;

public class HelloWorldConfiguration extends Configuration {
	@NotEmpty
	private String template;
	@NotEmpty
	private String defaultName = "Stranger";
	
	@JsonProperty
	public String getTemplate() {
		return template;
	}
	
	@JsonProperty
	public void setTemplate(String template) {
		this.template = template;
	}
	
	@JsonProperty
	public String getDefaultName() {
		return defaultName;
	}
	
	@JsonProperty
	public void setDefaultName(String defaultName) {
		this.defaultName = defaultName;
	}
	
}

2. Resourceapp

package com.example.helloworld.resources;

import java.util.concurrent.atomic.AtomicLong;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;

import com.codahale.metrics.annotation.Timed;
import com.example.helloworld.core.Saying;
import com.google.common.base.Optional;

@Path("/hello-world")
@Produces(MediaType.APPLICATION_JSON)
public class HelloWorldResource {
	private final String template;
    private final String defaultName;
    private final AtomicLong counter;
    
    public HelloWorldResource(String template, String defaultName) {
        this.template = template;
        this.defaultName = defaultName;
        this.counter = new AtomicLong();
    }
    
    @GET
    @Timed
    public Saying sayHello(@QueryParam("name") Optional<String> name) {
    	final String value = String.format(template, name.or(defaultName));
    	return new Saying(counter.incrementAndGet(), value);
    }
}

3. HealthCheck框架

package com.example.helloworld.health;

import com.codahale.metrics.health.HealthCheck;

public class TemplateHealthCheck extends HealthCheck {
    private final String template;

    public TemplateHealthCheck(String template) {
        this.template = template;
    }

    @Override
    protected Result check() throws Exception {
        final String saying = String.format(template, "TEST");
        if (!saying.contains("TEST")) {
            return Result.unhealthy("template doesn't include a name");
        }
        return Result.healthy();
    }
}

4. Applicationeclipse

package com.example.helloworld;

import com.example.helloworld.health.TemplateHealthCheck;
import com.example.helloworld.resources.HelloWorldResource;

import io.dropwizard.Application;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;

public class HelloWorldApplication extends Application<HelloWorldConfiguration>{
	
	public static void main(String[] args) throws Exception {
		new HelloWorldApplication().run("server", "hello-world.yml");
	}
	
	@Override
	public void run(HelloWorldConfiguration configuration, Environment environment) throws Exception {		
		final HelloWorldResource resource = new HelloWorldResource(
	        configuration.getTemplate(),
	        configuration.getDefaultName()
	    );
		environment.jersey().register(resource);
		
		final TemplateHealthCheck healthCheck = new TemplateHealthCheck(configuration.getTemplate());
		environment.healthChecks().register("template", healthCheck);
	}
	
	@Override
	public String getName() {
		return "hello-world";
	}

	@Override
	public void initialize(Bootstrap<HelloWorldConfiguration> bootstrap) {
		super.initialize(bootstrap);
	}

}

5. 返回的json格式的數據,例子maven

{
  "id": 1,
  "content": "Hi!"
}

6. 新建POJO類來封裝返回數據

package com.example.helloworld.core;

import org.hibernate.validator.constraints.Length;

import com.fasterxml.jackson.annotation.JsonProperty;

public class Saying {
	private long id;
	
	@Length(max=3)
	private String content;
	
	public Saying() {}
	
	public Saying(long id, String content) {
		this.id = id;
		this.content = content;
	}

	@JsonProperty
	public long getId() {
		return id;
	}

	@JsonProperty
	public void setId(long id) {
		this.id = id;
	}

	@JsonProperty
	public String getContent() {
		return content;
	}

	@JsonProperty
	public void setContent(String content) {
		this.content = content;
	}
	
}

6. 回到eclipse中,在HelloWorldApplication直接Running As-->Java Application,啓動服務

會輸出以下信息:

INFO  [2016-05-03 07:57:14,290] org.eclipse.jetty.util.log: Logging initialized @1236ms
INFO  [2016-05-03 07:57:14,373] io.dropwizard.server.ServerFactory: Starting hello-world
INFO  [2016-05-03 07:57:14,380] io.dropwizard.server.DefaultServerFactory: Registering jersey handler with root path prefix: /
INFO  [2016-05-03 07:57:14,397] io.dropwizard.server.DefaultServerFactory: Registering admin handler with root path prefix: /
INFO  [2016-05-03 07:57:14,433] org.eclipse.jetty.setuid.SetUIDListener: Opened application@136fbc5{HTTP/1.1}{0.0.0.0:8080}
INFO  [2016-05-03 07:57:14,434] org.eclipse.jetty.setuid.SetUIDListener: Opened admin@cb4636{HTTP/1.1}{0.0.0.0:8081}
INFO  [2016-05-03 07:57:14,436] org.eclipse.jetty.server.Server: jetty-9.2.13.v20150730
INFO  [2016-05-03 07:57:14,952] io.dropwizard.jersey.DropwizardResourceConfig: The following paths were found for the configured resources:

    GET     /hello-world (com.example.helloworld.resources.HelloWorldResource)

INFO  [2016-05-03 07:57:14,954] org.eclipse.jetty.server.handler.ContextHandler: Started i.d.j.MutableServletContextHandler@13cab44{/,null,AVAILABLE}
INFO  [2016-05-03 07:57:14,958] io.dropwizard.setup.AdminEnvironment: tasks = 

    POST    /tasks/log-level (io.dropwizard.servlets.tasks.LogConfigurationTask)
    POST    /tasks/gc (io.dropwizard.servlets.tasks.GarbageCollectionTask)

INFO  [2016-05-03 07:57:14,962] org.eclipse.jetty.server.handler.ContextHandler: Started i.d.j.MutableServletContextHandler@7715db{/,null,AVAILABLE}
INFO  [2016-05-03 07:57:14,976] org.eclipse.jetty.server.ServerConnector: Started application@136fbc5{HTTP/1.1}{0.0.0.0:8080}
INFO  [2016-05-03 07:57:14,977] org.eclipse.jetty.server.ServerConnector: Started admin@cb4636{HTTP/1.1}{0.0.0.0:8081}
INFO  [2016-05-03 07:57:14,978] org.eclipse.jetty.server.Server: Started @1925ms

當看到INFO  [2016-05-03 07:57:14,978] org.eclipse.jetty.server.Server: Started @1925ms時,表示服務啓動成功。

7. 打開瀏覽器,在地址欄中輸入(例子):http://localhost:8080/hello-world?name=zhangsan

能夠獲得以下信息:

{"id":1,"content":"Hello, zhangsan!"}

至此,經過dropwizard來快速部署RESTful Service服務完畢。

8. 執行邏輯分析

1). HelloWorldApplication --> main() --> initialize() --> getName()

先依次調用HelloWorldApplication的main()方法-->initialize()方法-->getName()方法

2). HelloWorldApplication --> run(HelloWorldConfiguration configuration, Environment environment)

由於HelloWorldApplication繼承了Application<HelloWorldConfiguration>,因此在調用HelloWorldApplication的run()方法以前dropwizard會經過讀取hello-world.yml文件來初始化run()方法的configuration參數。run()方法主要用來註冊用戶資源。

3). 執行HelloWorldApplication的run()方法,註冊用戶資源

好比當前的代碼中,註冊了用來整合返回值顯示模板和默認訪問用戶的HelloWorldResource和用來檢查自定義template是否符合標準的TemplateHealthCheck

4). 前三步執行完畢後,服務已經啓動成功

5). 瀏覽器地址欄輸入:http://localhost:8080/hello-world?name=zhangsan進行訪問

由於HelloWorldResource添加了@Path("/hello-world")和@Produces(MediaType.APPLICATION_JSON)註解,它的sayHello()方法添加了@GET方法,而且sayHello()方法的參數Optional<String> name添加了@QueryParam("name")註解,因此dropwizard接收到該瀏覽器訪問時,會將name=zhangsan的值賦給sayHello()方法的name參數,最後sayHello()方法根據所得的值,構建Saying類實例對象,返回給瀏覽器(以json格式)。

4、補充

經過maven將項目打包成jar包,達到能夠在cmd中能夠 java -jar xxx.jar來運行

1. 打開cmd,進入到maven項目的根目錄,執行mvn package

2. 打包成功後,根目錄下回多出一個target目錄,進入其中找到項目的jar包,如我當前的jar包名字是:dropwizardTest-0.0.1-SNAPSHOT.jar

3. 將dropwizardTest-0.0.1-SNAPSHOT.jar拷貝到一個指定目錄,如:test,同時須要將dropwizard環境配置文件也拷貝到test目錄下(當前個人配置文件時hello-world.yml)

4. 啓動cmd,進入到test目錄下,執行以下命令java -jar dropwizardTest-0.0.1-SNAPSHOT.jar,若是成功,則輸出信息也前面經過eclipse run啓動時輸出的信息基本一致。

相關文章
相關標籤/搜索