内容


RESTful Web 服务和 Apache Wink,第 3 部分

Apache Wink 和 REST

比较 Apache Wink 和其他开源 JAX-RS 实现

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: RESTful Web 服务和 Apache Wink,第 3 部分

敬请期待该系列的后续内容。

此内容是该系列的一部分:RESTful Web 服务和 Apache Wink,第 3 部分

敬请期待该系列的后续内容。

本文是一个三部分文章系列的第 3 部分,将比较 Apache Wink 和其他各种免费的开源 JAX-RS 实现,比如 Project Jersey、JBoss RESTEasy 和 Restlet Framework。本文将简要描述每个实现框架,同时基于一组公共属性强调它们之间的区别。最后,本文通过分析和审查这些不同的 JAX-RS 实现来帮助您选择适合自己需求的适当框架。

功能比较

那么,应该使用那些主要领域来比较这些不同的 JAX-RS 实现呢?针对本文的目的,我主要关注 5 个重要领域。显然,可以通过更多的特性来比较这些 JAX-RS 实现,但下面 5 个特性是进行快速、简便、高效的、基于 REST 的生产质量服务开发和测试的关键特性:

  • 嵌入式容器。多数 JAX-RS 实现都可以部署在一个 servlet 容器中,但有时需要在一个不基于 servlet 的简单 Java 应用程序中以一种嵌入式方式运行基于 REST 的服务。确保知道哪些实现支持使用嵌入式容器。
  • 客户端 API。JAX-RS 定义了复杂的服务器绑定规范,但规定由实现框架来负责定义客户端绑定和 APIs。因此,客户端架构和框架是选择 JAX-RS 实现的一个关键属性。
  • 侦听器框架。基于 REST 的 Web 服务开发人员经常需要以一种非侵入方式对 HTTP 调用进行预先处理和事后处理。这些调用对日志记录、缓存设置和安全验证等操作很有用。确定您的框架提供的、用于处理 HTTP 侦听的机制。
  • 数据格式支持。通过使用 MessageBodyReaderMessageBodyWriter 提供程序,JAX-RS 允许轻松添加对任意数据类型的支持。确定常用格式(包括 Atom、JSON 和 MIME 多部分数据)的开箱即用支持。
  • 组件集成。与其他框架的集成在基于 REST 的服务开发中很重要。通常,您使用 Spring 等其他框架进行依赖项注入,使用其他 Model-View-Controller (MVC) 框架来处理 UI。确定您选择的 JAX-RS 框架与第三方组件的原生集成支持。

Project Jersey

Project Jersey 是 Sun® 公司提供的、用于构建 RESTful Web 服务的、具有生产质量的双重授权(dual-licensed)开源 JAX-RS 参考实现。这意味着它不仅仅是一个参考实现,只提供允许开发人员轻松定制和扩展的 APIs。Jersey 作为 Sun 的 GlassFish 应用程序服务器下载的一部分提供。

嵌入式容器

Jersey 通常部署在一个 servlet 容器中,但不支持 Java 程序中的嵌入式操作模式。以嵌入式模式运行 JAX-RS 服务比较简单,只需几行代码。您还可以将这个可嵌入的容器轻松用于单元测试。

客户端 API

Jersey 客户端 API 是一个复杂的、基于 Java 的高级 API,用于调用任何 RESTful Web 服务,而不仅仅是与 JAX-RS 兼容的服务。但是,JAX-RS 开发人员应该会发现 Jersey 客户端 API 似曾相识,使用方便。Jersey 客户端 API 宣称其拥有 3 个重要目标:

  • 在客户端上封装 Uniform Interface Constraint 的 REST 约束。
  • 允许与服务器端 RESTful Web 服务轻松互操作。
  • 利用客户端的 JAX-RS API 概念和工件。

Jersey 客户端 API 还支持一个可插入 HTTP 实现(比如 HttpURLConnection 和 Apache HTTP 客户端)。总的来说,Jersey 客户端 API 允许您高效地实现一个基于 REST 的客户端解决方案。

清单 1 是一个 Jersey 客户端代码示例,它支持使用表单参数发送一个 POST 请求并接收一个作为 JAXB 对象的响应。

清单 1. Jersey 客户端代码
Form form = new Form(); 
f.add(“a”, “dim”); 
f.add(“b”, “sum”);
Client client = Client.create(); 
WebResource resource =   client.resource(“http://localhost:8080/formpost”);
JAXBBean bean = resource.
    type(MediaType.APPLICATION_FORM_URLENCODED_TYPE).
    accept(MediaType.APPLICATION_JSON_TYPE).
    post(JAXBBean.class, form);

注意,有一点很重要:如果这段代码使用 HttpURLConnection 编写,则会涉及许多序列化表单变量并将响应反序列化给 JAXB bean 的工作。

侦听器框架

Jersey 提供一个基于过滤器的侦听器框架,该框架允许注册两种类型的过滤器:

  • 容器过滤器。容器过滤器过滤资源过滤器前面的请求。
  • 资源过滤器。资源过滤器过滤容器过滤器前面的响应。

数据格式支持

与其他 JAX-RS 实现一样,Jersey 提供一些 JAX-RS 扩展模块来支持常用格式,包括 Atom、JSON 和 MIME 多部分数据。Atom 支持需要 Apache Abdera 上的一个依赖项以及 jersey-atom-abdera 模块。

组件集成

Jersey 目前对以下两个依赖项注入框架提供基于扩展的支持:Spring 框架和 Google Guice 框架。

  • Spring 框架。Jersey 中的 Spring 支持需要 jersey-spring 模块上的一个依赖项。Spring 支持通过在 web.xml 文件中引用 SpringServlet 类来启用。
  • Google Guice 框架。Guice 支持通过在 web.xml 文件中引用 Guice 过滤器 GuiceFilter和一个 Guice 特有的 ServletContextListener 来启用。

Apache Wink

如果您阅读过本文章系列前面的文章,您应该已经比较了解 Apache Wink。如果没有,下面是一个简要介绍。

Apache Wink 1.0 是一个全新设计的、与 JAX-RS 1.0 规范完全兼容的实现,它简单易用、生产就绪,提供了一组增强核心 JAX-RS 规范的特性,包括:

嵌入式容器

Apache Wink 1.0 的设计目的是为了在一个 servlet 中运行,目前不支持嵌入式操作模式。但是,任何兼容的轻量级 servlet 容器应该能够支持 Apache Wink 运行时。

客户端 API

Apache Wink 包含一个复杂的内置客户端框架,该框架提供一个简单的 Java API,以支持轻松直观地实现一些客户端来使用基于 HTTP 的 RESTful Web 服务。即便是作为一个独立的基于 REST 的 Java 客户端框架,Wink 客户端框架也很有用。

侦听器框架

Apache Wink 运行时利用句柄链(handler chains)来处理请求。具体来说,有 3 种句柄链:请求响应错误。要定制句柄链,可在应用程序的 web.xml 文件中扩展 org.apache.wink.server.handlers.HandlersFactory 类,覆盖方法并指定新的句柄库类。

数据格式支持

Apache Wink 1.0 绑定了一组内置提供程序,它们可以协助您支持各种行业标准数据格式,包括 XML、Atom、Atom Publishing Protocol (APP)、RSS、JSON、CSV、HTML、OpenSearch 和 multipart。

组件集成

Apache Wink 通过核心框架附带的一个附加模块来提供轻松 Spring 集成。Apache Wink Spring 集成模块提供各种特性,包括:

  • 将资源和提供程序注册为类或 Spring beans。
  • 资源/提供程序生命周期定制。
  • 能够使用一些 Spring 特性,比如 Inversion of Control (IoC)。
  • 从 Spring 上下文中使用 hooks 进行轻松定制。

Apache Wink 还通过一个扩展模块来支持 WebDAV 协议,该扩展模块有助于 WebDAV 响应的创建和处理。清单 2 是使用 WebDAVResponseBuilder 类来实现一个 JAX-RS 资源的示例,该资源与使用 WebDAVMethod.PROPFIND 符号的 PROPFIND WebDAV HTTP 方法关联。

清单 2. Apache Wink WebDAVResponseBuilder 示例
@Path("books/{bookid}")
public class BookResource {
  @WebDAVMethod.PROPFIND
  @Consumes("application/xml")
  @Produces(application/xml")
  public Response propfindBook(@PathParam("bookid") String booked) {
    SyndFeed feed = ...
    return WebDAVResponseBuilder.propfind(feed);
  }
}

JBoss RESTEasy

JBoss RESTEasy 是 Red Hat® 提供的 JAX-RS 兼容框架实现,它受到 GNU Lesser General Public License (LGPL) 许可,可用于任何基于 servlet 的环境。

嵌入式容器

JBoss RESTEasy 实现附带一个轻量级的可嵌入 servlet 容器 TJWS,可用于在单元测试中对 JAX-RS Web 服务进行远程调用。可以使用这个嵌入式容器来运行单元测试,而不必运行整个 servlet 容器。清单 3 是在 RESTEasy 中使用这个可嵌入容器的一个示例。

清单 3. JBoss RESTEasy 嵌入式服务器示例
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import org.jboss.resteasy.plugins.server.tjws.TJWSEmbeddedJaxrsServer;
import org.jboss.resteasy.spi.ResteasyDeployment;
import org.jboss.resteasy.client.ClientRequest;

public class MainResource {
   @Path("restresource")
   public static class MyRestResource {
   @GET
   @Produces("text/plain")
      public String get() {
         return "Hello World";
      }
   }
public static void main(String[] args) throws Exception {
      TJWSEmbeddedJaxrsServer server = new TJWSEmbeddedJaxrsServer();
      server.setPort(8080);
      server.getDeployment()
            .getRegistry().addPerRequestResource(MyRestResource.class);
      try {
         ClientRequest request =
               new ClientRequet("http://localhost:8080/restresource");
         String message = request.getTarget(String.class);
         System.out.println(message);
      } finally {
         server.stop();
      }
   }
}

客户端 API

RESTEasy 提供一个编程式客户端 API 来提交 HTTP 请求,但同时具有 JAX-RS 意识。要进行客户端调用,必须创建 org.jboss.resteasy.client.ClientRequest 类的一个实例。RESTEasy 客户端框架运行在 Apache HttpClient 之上,但也支持将 java.net.URLConnection 用作一个主干(backbone)。RESTEasy 还拥有一个内置的客户端代理框架,该框架是一种略微不同的编写 RESTful Java 客户端的方法,其工作方式是在客户端重用 JAX-RS 符号,将一个方法调用转换为一个对等的 HTTP 请求。

侦听器框架

RESTEasy 使用称为侦听器(interceptor) 的监听器对象,侦听器能够监听 JAX-RS 调用并重新路由它们。根据要监听的调用过程的不同阶段,侦听器可以分为 4 种不同类型:MessageBodyReader 侦听器、MessageBodyWriter 侦听器、预处理侦听器和后处理侦听器。客户端还有不同类型的侦听器。

数据格式

与此前讨论的其他 JAX-RS 实现类似,RESTEasy 支持大多数流行的数据格式,包括 XML、JSON、Atom、XML-binary Optimized Packaging (XOP)、Fastinfoset 和 multipart。但是,由于 Abdera Project 不提供 JAXB 内容支持,RESTEasy 还提供一个简单的、带有 JAXB 注释的对象模型来提供 Atom 支持。

组件集成

作为一个以 JBoss 驱动的实现,RESTEasy 支持与 JBoss Seam 的紧密集成一点都不奇怪。但是,它还支持与其他流行框架和标准的集成,比如 Enterprise JavaBean (EJB) 技术(一个 Java Platform, Enterprise Edition [Java EE] 标准)、Spring 和 Google Guice。

Restlet Framework

Restlet Framework 与您前面看到的其他 JAX-RS 实现略微有些不同,因为在 JAX-RS 最终成为一个标准之前它就已经存在了。它被设计为一个轻量级的、基于 REST 的 Java 框架,通过一些可插入的扩展来实现不同的功能。在较高的层面上,Restlet Framework 包含 3 个组成部分:

  • Restlet API
  • 实现这个 API 的 Noelios Restlet Engine (NRE)
  • 可选的 Restlet 扩展

扩展作为实现 JAX-RS 规范的 Restlet Framework 组成部分可用,用于提供您已知并喜爱的所有 JAX-RS 特性。

嵌入式容器

在很大程度上,Restlet Framework 总是独立于协议的,这允许它在不同的部署配置下运行,包括独立 JAR 文件、servlet 容器、Spring 容器以及 Google Web Toolkit。除了能够利用 Spring XML 配置机制外,Restlet Framework 还支持它自己的基于 XML 的配置。

客户端 API

Restlet 包含一个客户端 API,支持轻松使用任何基于 HTTP 的远程服务,而不仅仅是 JAX-RS 服务。Restlet Framework 基于一个连接器和组件架构,其中一个连接器(通常通过实现一个网络协议)支持组件之间的通信。通过实例化特定于所需的协议的 org.restlet.Client 类的一个对象,可以调用远程 HTTP 服务。

侦听器框架

Restlet Framework 使用一种基于路由器的复杂机制来路由应用程序内的 URI 调用。路由器是一个 Restlet 或资源,它将 URI 关联到处理对这个 URI 的所有请求的资源。 通过扩展抽象类 org.restlet.Filter 并将其附加到一个现有路由器,可以侦听对路由器的调用。过滤器支持在某个目标 Restlet 对一个调用的处理之前或之后进行一些处理。清单 4 是一个展示路由器的默认用途的示例,它声明两个路由 — 一个将 URI /books 关联到资源 BooksResource,另一个将 URI /books/bookName 关联到资源 BookResource

清单 4. Restlet 路由器创建
// Create a router Restlet that defines routes.
Router router = new Router(getContext());

// Defines a route for the resource "list of Books"
router.attach("/books", BooksResource.class);
// Defines a route for the resource "book"
router.attach("/books/{bookName}", BookResource.class);

数据格式

Restlet Framework 使用对其核心框架的扩展来支持主要的数据格式,比如 XML、JSON 和 Atom。用于 Atom 的 Restlet 扩展为提要和发布提供了一个综合 Atom API,这个 API 能够解析并格式化 Atom 和 APP XML 文档。

组件集成

借助其广泛的扩展库,Restlet Framework 提供与各种框架和标准的集成支持,比如 Spring、Jetty、Grizzly、Simple、JAXB、JAX-RS、JiBX、Velocity 和 FreeMarker。

性能比较

没有某种形式的性能测试,任何软件框架比较都不是完整的。这里有一个性能方法系统的简要描述,该方法系统用于执行一个非常不正式的性能测试,以便衡量各种 JAX-RS 实现的相对流量。注意,这个测试并不意味着是这些 JAX-RS 框架之间的正式基准测试,只是为了向您提供它们的相对性能特征的一般认识。

  • 性能标准。我使用事务性流量(通常以 “事务/秒” 为计量单位)作为指标。在本例中,它意味着对运行在目标 JAX-RS 框架上的 RESTful 服务的单个基于 REST 的 GET 请求。
  • 测试框架。我使用一个开源性能测试框架 Grinder 3.3 版来模拟客户端请求负载。
  • 采样方法。采样方法定义如何收集实际测试指标。在本例中,我使用 cycle 方法,其中,我以固定的周期数运行测试,一个周期定义为测试脚本的一次完整执行。在这个性能测试中,我使用一个固定的周期长度 1500,这个长度足够大,能够在统计数据上造成影响。
  • 测试服务/系统。为测试这些 JAX-RS 框架的性能特征,我选择了一个简单的 JAX-RS 测试服务 —— 该服务不过是一个经过美化的 “HelloWorld” 服务,并将其移植到各种 JAX-RS 框架上。请参见 下载 部分获取使用的 JAX-RS 资源的源代码。所有 JAX-RS 框架都作为 Web 应用程序在一个 Jrockit version 1.5 Java Virtual Machine (JVM) 上运行的一个未调优的 Apache Tomcat 服务器中运行。清单 5 是用于针对其进行测试的样例 RESTful 服务。
    清单 5. 用于性能测试的 JAX-RS 服务
    …….
    
    // The Java class will be hosted at the URI path "/transactions"
    @Path("/transactions")
    public class TransactionResource {
    
     @GET
     @Path("{pnref}")
     @Produces(MediaType.TEXT_HTML)
     public String doInquiry(@PathParam("pnref") String pnref) {
      try {
    	return getMockTxnStatus(pnref).toString();
      } catch (Exception e) {
    	throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
      }
     }
    	
     private JSONObject getMockTxnStatus(String pnref) throws JSONException {        
    
      JSONObject jsonObject = new JSONObject();
      jsonObject = jsonObject
       .put("RESULT","127")
       .put("PNREF",pnref)
       .put("RESPMSG","DummyResponse")
       .put("AUTHCODE","ASSDS")
       .put("CVV2MATCH","true")
       .put("AVSADDR","dummyaddress")
       .put("AVSZIP","dummyzip")
       .put("IAVS","IAVS")
       .put("CARDSECURE","cardsecure");
      JSONObject retObject = new JSONObject();
    	
      for(int i=0;i<100;i++) 
     	retObject.put("jsonbj"+i, jsonObject);
    
      return retObject;
    
     }
        
    }
  • 测试运行。我分别在每个 JAX-RS 实现框架上运行这个性能测试。样例 JAX-RS 服务被移植到每个 JAX-RS 框架,测试测量每个框架上的 1500 个周期末端的平均流量和峰值流量。图 1 展示了性能测试的结果。
    图 1. 性能测试结果
    一个条形图,显示 JAX-RS 实现框架性能测试结果。X 轴表示性能指标;Y 轴表示每秒事务数。Peak TPS 的条形组要比 Mean TPS 的条形组高。
    一个条形图,显示 JAX-RS 实现框架性能测试结果。X 轴表示性能指标;Y 轴表示每秒事务数。Peak TPS 的条形组要比 Mean TPS 的条形组高。
  • 性能测试的分析。图 1 所示,除 Restlet Framework 框架外,其他大多数 JAX-RS 实现拥有相似的流量指标。有些实现比其他实现稍好一些 — 比如,Apache Wink 拥有最高的峰值 TPS 指标,而 Project Jersey 拥有最高的平均 TPS。另外,平均 TPS 和峰值 TPS 之间的相对顺序基本相同。根据这些性能指标,您可以得出这样的结论:Apache Wink、JBoss RESTEasy 和 Project Jersey 拥有相似的性能特征,而 Restlet Framework 的性能比它们略低一些。但是应该注意一点,由于这不是正式的 JAX-RS 框架基准测试,改变负载和测试条件可能会导致不同的结果。

结束语

本文简要介绍了多个 JAX-RS 实现,包括 Project Jersey、Apache Wink、JBoss RESTEasy 和 Restlet Framework。本文使用了 5 个主要的功能属性来描述每个框架:嵌入式容器、客户端 API 框架、侦听器框架、数据格式支持和组件集成支持。尽管本文讨论的每个框架都实现相同的 JAX-RS 规范,但这些框架的设计和架构的差别非常大。

另外,我使用了一个样例 RESTful 服务和一个开源性能测试工具在所有这些 JAX-RS 框架上运行了一个非正式性能测试。我分析了性能测试的结果,以比较和对比各种实现的运行时特征。

根据您的特殊需求,您可能会发现上述框架中的一个或几个最适合您。例如,如果组件集成的重要性最大,Restlet Framework 或 RESTEasy 可能是最好的选择。但是,如果广泛的数据格式支持和一个具有高流量特征的复杂侦听框架更重要,那么 Apache Wink 可能更适合您的需要。我希望本文能够帮助您理解市面上的各种开源 JAX-RS 实现之间的区别,从而找出最适合您的需要的实现。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Web development
ArticleID=555997
ArticleTitle=RESTful Web 服务和 Apache Wink,第 3 部分: Apache Wink 和 REST
publish-date=10252010