博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
在dropwizard中使用feign,使用hystrix
阅读量:7167 次
发布时间:2019-06-29

本文共 16539 字,大约阅读时间需要 55 分钟。

前言

用惯了spring全家桶之后,试试dropwizard的Hello World也别有一帆风味。为了增强对外访问API的能力,需要引入open feign。这里简单在dropwizard中使用feign。

1. 什么Dropwizard

Dropwizard is a Java framework for developing ops-friendly, high-performance, RESTful web services.

Dropwizard使成熟、稳定的java生态系统更加简单、轻量(light-weight), 让你更专注于业务逻辑。

Dropwizard 为配置(configuration)、统计(application metrics)、日志(logging)、operational tools提供了开箱即用的能力。让您和您的团队能够在最短的时间内开发出具有生产环境的质量的Web服务。

下面的简介来自

DropWizard是由Yammer开发团队贡献的一个后台服务开发框架,其集成了Java生态系统中各个问题域中最优秀的组件,帮助开发者快速的打造一个Rest风格的后台服务。

对开发者来说,使用DropWizard有如下好处:

1、和Maven集成良好,也就是说和Gradle集成也很良好;
2、开发迅速,部署简单;
3、代码结构好,可读性高;
4、自动为服务提供OM框架;
5、让开发者自然的把一个应用拆分为一个个的小服务

DropWizard结构的Web服务组成

1、Configuration:用于设置该服务的配置,比方说在服务开放在哪个端口,数据库配置是怎样的等等。
2、Application(即Service):该服务的主入口,定义该服务使用哪个配置文件,开放哪些Resource,该服务需要哪些HealthCheck等等。
3、Resource:定义一个资源,包括如何获取该资源,对该资源做Get/Post/Delete/Query时,对应的各种业务逻辑。
4、Representation:定义了一个服务返回值对象,当服务返回该对象时,会自动的把该对象按属性值生成一个Json格式的字符串返回给服务调用者。
5、HealthCheck:在DropWizard为每个服务提供的OM框架中用到,通过它可以随时检测当前服务是否可用。

Dropwizard内置了Jetty

Web应用程序不能没有HTTP,所以Dropwizard使用Jetty HTTP库将一个令人难以置信的HTTP服务器直接嵌入到您的项目中。 Dropwizard项目不需要将应用程序交给一个复杂的应用程序服务器,而是一个main方法,它会自动连接一个HTTP服务器。将应用程序作为一个简单的过程运行,消除了Java在生产中的一些不好的东西(没有PermGen问题,没有应用程序服务器配置和维护,没有复杂的部署工具,没有类加载器(class loader)故障,没有隐藏的应用程序日志,没有尝试调整一个垃圾收集器来处理多个应用程序工作负载),并允许您使用所有现有的Unix进程管理工具。

Dropwizard 使用Jersey提供Rest能力

Dropwizard 使用Jackson来处理json

Dropwizard 提供了类库

2. Hello World For Dropwizard

吹完牛逼,开始干活。

照例,首先本次测试(:

.├── dependency-reduced-pom.xml├── l4dropwizard.iml├── pom.xml├── readme.md└── src    └── main        ├── java        │   └── com        │       └── test        │           ├── HelloWorldApplication.java        │           ├── configuration        │           │   ├── HelloWorldConfiguration.java        │           │   └── modules        │           │       ├── ConnectAndReadConfig.java        │           │       └── GithubApiConfig.java        │           └── domain        │               ├── connect        │               │   ├── GithubClient.java        │               │   └── GithubConnector.java        │               ├── entiry        │               │   ├── GithubUser.java        │               │   └── Saying.java        │               ├── health        │               │   └── TemplateHealthCheck.java        │               └── resource        │                   ├── GithubResource.java        │                   └── HelloWorldResource.java        └── resources            └── config                └── dev.yml14 directories, 16 files

2.1 添加依赖

依旧是maven项目,pom中添加dropwizard

1.0.6
1.8
com.test.HelloWorldApplication
io.dropwizard
dropwizard-core
${dropwizard.version}

2.2 添加配置中心

dropwizard采用yaml作为配置文件,同时需要有个配置类对应yaml中的属性。

创建config/dev.yml

template: Hello, %s!defaultName: Strangerserver:#  softNofileLimit: 1000#  hardNofileLimit: 1000  applicationConnectors:    - type: http      port: 8080    #this requires the alpn-boot library on the JVM's boot classpath    #- type: h2    #  port: 8445    #  keyStorePath: example.keystore    #  keyStorePassword: example  adminConnectors:    - type: http      port: 8082

然后,新建对应的配置类com.test.configuration.HelloWorldConfiguration

package com.test.configuration;import com.fasterxml.jackson.annotation.JsonProperty;import io.dropwizard.Configuration;import org.hibernate.validator.constraints.NotEmpty;/** * Created by rmiao on 3/14/2017. */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 name) {        this.defaultName = name;    }}

下一步就是启动类:com.test.application.HelloWorldApplication

package com.test;import com.test.domain.health.TemplateHealthCheck;import com.test.domain.resource.HelloWorldResource;import com.test.configuration.HelloWorldConfiguration;import io.dropwizard.Application;import io.dropwizard.setup.Bootstrap;import io.dropwizard.setup.Environment;import java.util.Map;/** * Created by Ryan Miao on 3/14/2017. */public class HelloWorldApplication extends Application
{ public static void main(String[] args) throws Exception { new HelloWorldApplication().run(args); } @Override public String getName() { return "hello-world"; } @Override public void initialize(Bootstrap
bootstrap) { // nothing to do yet } @Override public void run(HelloWorldConfiguration configuration, Environment environment) throws Exception { final HelloWorldResource resource = new HelloWorldResource( configuration.getTemplate(), configuration.getDefaultName() ); final TemplateHealthCheck healthCheck = new TemplateHealthCheck(configuration.getTemplate()); environment.healthChecks().register("template", healthCheck); environment.jersey().register(resource); environment.jersey().register(healthCheck); }}

到此,配置基本完成,只需要添加接口resource就好。

2.3 创建第一个API

对应于springmvc中conroller, dropwizard采用jersey,使用resourc作为接口类:com.test.com.test.resource.HelloWorldResource

package com.test.domain.resource;import com.codahale.metrics.annotation.Timed;import com.test.domain.entiry.Saying;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 java.util.Optional;import java.util.concurrent.atomic.AtomicLong;/** * Created by rmiao on 3/14/2017. */@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
name) { final String value = String.format(template, name.orElse(defaultName)); return new Saying(counter.incrementAndGet(), value); }}

这里的template没啥意思,官网用在这里就是为了彰显下读取配置文件的能力: 通过configuration类来操作配置属性。

另外,需要注意的是,resource并不能像Spring一样自动扫描,需要手动去environment.jersey().register(resource);

2.4 启动

启动前还需要配置fat jar,同Spring-boot一样,fat jar首选. 在pom配置:

maven-compiler-plugin
3.5.1
${java.version}
${java.version}
UTF-8
org.apache.maven.plugins
maven-jar-plugin
3.0.2
true
org.apache.maven.plugins
maven-shade-plugin
2.3
true
*:*
META-INF/*.SF
META-INF/*.DSA
META-INF/*.RSA
package
shade
${mainClass}
org.codehaus.mojo
exec-maven-plugin
1.6.0
${mainClass}
server
target/classes/config/dev.yml
application.name
HelloWorld
application.home
.
application.environment
dev

接下来,打包:

mvn package

然后,run jar:

java -jar target\l4dropwizard-1.0-SNAPSHOT.jar server target/classes/config/dev.yml

浏览器访问http://localhost:8080/hello-world?name=Ryan

将得到:

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

至此,hello world完成。

什么是Feign

Feign是一个网络请求客户端,简化了网络请求代码,使得我们可以采用更加友好的方式发送请求,并且管理请求。Feign采用注解驱动模板,所以目前只支持text-based apis.

Dropwizard with Feign

依赖

首先,添加依赖:

io.github.openfeign
feign-core
${feign.version}
io.github.openfeign
feign-hystrix
${feign.version}
io.github.openfeign
feign-slf4j
${feign.version}
io.github.openfeign
feign-jackson
${feign.version}
io.github.openfeign
feign-gson
${feign.version}
io.reactivex
rxjava
1.2.1
compile

配置

Feign的配置主要有三个,一个是isolation.thread线程存活时间。一个是connectTimeoutMillis连接超时,一个是readTimeoutMillis

本次测试将采用github的公共API,获取用户信息。首先配置线程存活时间。在dev.yml中添加:

hystrixConfig:  hystrix.command.GithubConnector#getUserProfile(String).execution.isolation.thread.timeoutInMilliseconds: 7000

然后是两个超时配置:

githubApiConfig:  baseUrl: "https://api.github.com"  getUserProfile:    connectTimeoutMillis: 2000    readTimeoutMillis: 5000

Dropwizard通过配置类和配置文件绑定的方式获取配置内容。因此,需要对应的在配置类中创建对应的字段。

com.test.configuration.modules.ConnectAndReadConfig

package com.test.configuration.modules;/** * Created by Ryan Miao on 9/14/17. */public class ConnectAndReadConfig {    private int connectTimeoutMillis;    private int readTimeoutMillis;    public int getConnectTimeoutMillis() {        return connectTimeoutMillis;    }    public int getReadTimeoutMillis() {        return readTimeoutMillis;    }}

com.test.configuration.modules.GithubApiConfig

package com.test.configuration.modules;/** * Created by Ryan Miao on 9/14/17. */public class GithubApiConfig {    private String baseUrl;    private ConnectAndReadConfig getUserProfile;    public String getBaseUrl() {        return baseUrl;    }    public ConnectAndReadConfig getGetUserProfile() {        return getUserProfile;    }}

在com.test.configuration.HelloWorldConfiguration中添加:

@NotEmptyprivate Map
hystrixConfig;@NotNullprivate GithubApiConfig githubApiConfig;

然后在application中配置好hystrix的配置:

在HelloWorldApplication#run方法中

//init hystrix configMap
hystrixConfig = configuration.getHystrixConfig();for (final Map.Entry
config : hystrixConfig.entrySet()) { ConfigurationManager.getConfigInstance().setProperty(config.getKey(), config.getValue());}

创建Feign的connector接口

创建接口com.test.domain.connect.GithubConnector:

package com.test.domain.connect;import com.test.domain.entiry.GithubUser;import feign.Headers;import feign.Param;import feign.RequestLine;import rx.Observable;/** * Created by ryan on 9/14/17. */public interface GithubConnector {    /**     * @param username     * @return     */    @RequestLine("GET /users/{username}")    @Headers({
"Accept: application/vnd.github.v3+json"}) Observable
getUserProfile(@Param("username") String username);}

创建调用客户端

创建客户端com.test.domain.connect.GithubClient

package com.test.domain.connect;import com.test.configuration.modules.ConnectAndReadConfig;import com.test.configuration.modules.GithubApiConfig;import com.test.domain.entiry.GithubUser;import feign.Request;import feign.Response;import feign.gson.GsonDecoder;import feign.gson.GsonEncoder;import feign.hystrix.HystrixFeign;import feign.slf4j.Slf4jLogger;import org.apache.commons.io.IOUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import rx.Observable;import java.io.IOException;import java.util.UUID;/** * Created by Ryan Miao on 9/14/17. */public class GithubClient {    public static final Logger LOGGER = LoggerFactory.getLogger(GithubClient.class);    private GithubApiConfig githubApiConfig;    public GithubClient(GithubApiConfig githubApiConfig) {        this.githubApiConfig = githubApiConfig;    }    public Observable
getUserProfile(String username) { String baseUrl = githubApiConfig.getBaseUrl(); ConnectAndReadConfig getUserProfile = githubApiConfig.getGetUserProfile(); GithubConnector connector = HystrixFeign.builder() .decoder(new GsonDecoder()) .encoder(new GsonEncoder()) .logger(new Slf4jLogger()) .options(new Request.Options(getUserProfile.getConnectTimeoutMillis(), getUserProfile.getReadTimeoutMillis())) .errorDecoder((methodKey, response) -> { StringBuilder msg = new StringBuilder("status=").append(response.status()) .append(";request_headers=").append(response.request().headers()) .append(";response_headers=").append(response.headers()) .append(";body="); Response.Body body = response.body(); if (body != null && body.length() > 0) { try { msg.append(IOUtils.toString(body.asReader())); } catch (IOException e) { msg.append("can not read body,"+e.getMessage()); } } return new RuntimeException(msg.toString()); }) .requestInterceptor(template -> template.header("requestId", UUID.randomUUID().toString())) .target(GithubConnector.class, baseUrl); return connector.getUserProfile(username).onErrorReturn(error -> { LOGGER.error("Get github user profile failed. ", error); return null; }); }}

创建一个接口测试

最后,创建一个接口来测试下:

com.test.domain.resource.GithubResource

package com.test.domain.resource;import com.codahale.metrics.annotation.Timed;import com.test.configuration.modules.GithubApiConfig;import com.test.domain.connect.GithubClient;import com.test.domain.entiry.GithubUser;import javax.ws.rs.GET;import javax.ws.rs.Path;import javax.ws.rs.PathParam;import javax.ws.rs.Produces;import javax.ws.rs.core.MediaType;/** * Created by Ryan Miao on 9/14/17. */@Path("/github")@Produces(MediaType.APPLICATION_JSON)public class GithubResource {    private GithubApiConfig githubApiConfig;    public GithubResource(GithubApiConfig githubApiConfig) {        this.githubApiConfig = githubApiConfig;    }    @GET    @Timed    @Path("/users/{username}")    public GithubUser getUserProfile(@PathParam("username") final String username){        GithubClient client = new GithubClient(githubApiConfig);        return client.getUserProfile(username).toBlocking().first();    }}

run main方法启动。访问localhost:8080/github/users/Ryan-Miao就可以得到我的github信息了:

{
"login": "Ryan-Miao", "id": 11866078, "avatar_url": "https://avatars3.githubusercontent.com/u/11866078?v=4", "url": "https://api.github.com/users/Ryan-Miao", "name": "Ryan Miao", "email": null, "location": "中国深圳", "blog": "https://ryan-miao.github.io/"}

至此,feign的简单集成就搞定了。

一些注意事项

feign采用hystrix的配置的时候,grop key是baseUrl.上栗中,grop Key为https://api.github.com, commandKey为接口+方法和参数,上栗中为GithubConnector#getUserProfile(String)。因此,配置线程超时用了commandKey如上。如果要配置coreSize之类的,必须使用url做为group key了。

本文转自Ryan.Miao博客园博客,原文链接:http://www.cnblogs.com/woshimrf/p/dropwizard-feign.html,如需转载请自行联系原作者

你可能感兴趣的文章
关于springmvc中DispatcherServlet问题
查看>>
Pentaho6.1 CCC/CDE tool 的Extension points属性详解
查看>>
jdk、tomcat的安装
查看>>
4月4日作业
查看>>
大数据MapReduce 编程实战
查看>>
计算机网络socket编程之UDP
查看>>
高性能的智能日志
查看>>
APScheduler BackgroundScheduler
查看>>
lvs-nat与lvs-dr配置
查看>>
『中级篇』容器的技术概述(二)
查看>>
Apache awstats安装报错解决过程适合初学者
查看>>
Vsftp安装及配置虚拟用户
查看>>
JVM内存区域
查看>>
DNS的视图功能的简单配置。
查看>>
linux和windows互传文件/用户配置文件和密码配置文件/用户组管理/用户管理
查看>>
通过javascript把图片转化为字符画
查看>>
OpenJPA 一些难搞的查询
查看>>
设置button的样式,使得按钮的图片在上面,文字在图片的下面
查看>>
MySQL之函数、存储过程和触发器
查看>>
完整版的OpenLDAP搭建全过程
查看>>