1
回答
使用 Gretty 的超轻量级 Java Web 服务
终于搞明白,存储TCO原来是这样算的>>>   

文章出处:IBM developerWorks

Gretty 是构建 Web 服务超轻量级框架的新学派之一。构建于极快的 Java™ 之上,Gretty 将 Groovy 用作 Web 端点和 Grape 的 Maven 式依赖关系管理的一种域特定语言。本文介绍如何开始使用 Gretty 来构建和部署 Java Web 服务应用程序。

我们已经构建了一个简单的从云到移动终端的应用程序。该应用程序名为 Magnus,充当监听移动设备位置信息的 HTTP 端点。 它通过接收 HTTP PUT 请求来运行,每个请求包含一个 JSON 文档,指出给定时间内帐户的位置。到目前为止,我已经使用了 Web 框架 Play 来开发和扩展 Magnus。

Play 提供一个 MVC 堆栈,就这点它与 Grails 很相似。使用 Play,您可以很容易地定义利用视图(JSP、GSP、模板等)的控制器(servlets),在某种程度上,控制器管理模型。模型是使用经 Hibernate、JPA 或其他类似 ORM 的好技术增强的 POJO(传统 Java 对象)实现的。

尽管 MVC 是较老的标准,随着 Grail 和 Play 等框架的出现,很多都已经发生了改变。回想一下曾经维护简单的 Web 请求-响应交互所需的工作量(比方说使用 Struts),您会明白我们为快速构建 MVC Web 应用程序而做了多大的改进。当然,并非所有的 Web 应用程序都需要 MVC 基础架构才能工作。如今,一些 Web 应用程序根本就不再需要 MVC “堆栈”。

为了反对这样一种反常的论调,在您关闭浏览器之前,回顾一下 Magnus。虽然为了演示,对 Magnus 进行了严格的设计,我的云到移动终端的应用程序不包含传统的视图组件,主要包含了现有的成功服务的模型。与 Twitter 或 Foursquare 一样,Magnus 接收来自世界各地不同设备的消息。广义上说,Magnus 是一个 Web 服务,而并不是每个 Web 服务都需要 MVC 堆栈框架才能完成工作。在某些情况下,您所需要的是一个超级轻量的 Web 框架,而不是 Web 堆栈。

本月,我们将着眼于以下内容之一:快速开发框架,太新以至于还没有自己的主页,或许并不需要主页。Gretty 的沿袭和隶属成员(分别包括 Netty 和 Groovy)具有足够的名望,它已经是 Java 2.0 Web 开发系列的一部分。它填补了一个许多开发人员仍然不知道他们已经具有的需求(这就是真正的 Web 2.0 风格,您知道吗?)。如果您愿意走狂野的一面,它也可足够稳定地用作生产之用。

快速 Java 开发的历史

老的足以记得何时第一次引入 Servlets API 的我们有理由对新的 “轻量级” 范式持怀疑态度;毕竟仅仅一个简单的 servlet 便让您构建一个 Web 服务,而不需要大量的代码和由此产生的 JAR 文件。Web 服务框架,比如 Restlet 或 Jersey,采取了稍微不同的开发加速方法,以类扩展、注释,甚至标准的 JSR 为基础来创建 RESTful Web 服务。在某些情况下,它们仍然是很好的选择。

但事实证明,一些新的轻量级(相对于旧的 轻量级)框架使得 Web 服务或简单的 HTTP 端点(也称为路由)极其易于定义。甚至比手动塞入一个 servlet 还要简单!

这些框架首次出现在其他平台上,尤其是用于 Ruby 的 Sinatra 和用于 Node.js 的 Express。但是针对 Java 平台的有趣项目也已经开始出现了。Gretty 就是其中之一,当然 Gretty 是为 Groovy 和 JVM 产生的。

我和 Gretty

就我而言,Gretty 至少有两点符合:首先是使用 Groovy 的 Grape(我不久将会详细地对其进行描述)以方便依赖性管理。其次是其简单的用于定义端点的 DSL 式的语法。 使用 Gretty,您可以非常快地(只用短短的几行代码)定义和部署一个工作的 Web 运行框架,该框架处理实际的业务逻辑。作为示例,请看我快速地写出清单 1 中的典型 hello world 示例:


清单 1. Hello, World:这就是 Gretty!
import org.mbte.gretty.httpserver.* 

@GrabResolver(name='gretty', 
  root='http://groovypp.artifactoryonline.com/groovypp/libs-releases-local')
@Grab('org.mbte.groovypp:gretty:0.4.279') 

GrettyServer server = [] 
server.groovy = [ 
    localAddress: new InetSocketAddress("localhost", 8080), 
    defaultHandler: { 
        response.redirect "/" 
    }, 
    "/:name": {
        get {
            response.text = "Hello ${request.parameters['name']}"
        } 
    } 
] 
server.start()

在 清单 1 中,我创建了一个服务器监听端口 8080,然后设置一个包含参数 name 的简单 root 端点。到其他端点的任何请求都将通过 defaultHandler 返回到 /。简单地说,使用 / 的位置,处理程序为请求的客户端发送一个 HTTP 301 “moved permanently” 代码 。所有请求会收到一个包含字符串 “Hello” 和任何已传递参数值的响应(将 content-type 设置为 text/plain);例如,/Andy 将会生成 “Hello Andy”。

那么 清单 1 中最有趣的是什么?首先,您在清单中所看到的都是您的应用程序所需要的。没有配置文件。不需要直接下载或安装任何东西(除了 Groovy 1.8)。要激活该示例,只要输入 groovy server.groovy

现在如果您的响应要求更复杂的文本而不是简单文本,该怎么办?对于该问题,Gretty 有很多选择,其中有两个是十分简单的。第一个是您可以简单地将响应类型设置为 HTML,正如我在清单 2 中所执行的:


清单 2. Gretty 中的 HTML 响应
				
"/:name": {
 get {
  response.html = "Hello ${request.parameters['name']}"
 } 
} 

在这种情况下,响应的 content-type 会被设置为 text/html。另外,Gretty 可以利用静态和动态的模板。例如,我可以使用一个类似于 JSP/GSP 的简单构造函数来定义模板,类似于清单 3:


清单 3. Gretty 中的 HTML 模板
<html>
 <head>
  <title>Hello!</title>
 </head>
 <body>
  <p>${message}</p>
 </body>
</html>

然后可以在某个响应的主体部分引用该模板:

清单 4. Gretty 中的 Groovy++ 模板
"/:name" {
  get {
   response.html = template("index.gpptl", 
     [message: "Hello ${request.parameters['name']}"])
  }
}

Getty 和 Grape 的依赖性管理

Gretty 令人难忘的开发速度都归功于 Grape,Gretty 使用 Grape 来自动下载二进制文件的依赖关系或 JAR 文件。使用 Maven 的传递依赖来加载所有文件。在 清单 1 中我所需要做的就是输入注释 @Grab('org.mbte.groovypp:gretty:0.4.279'),然后我会获得与 Gretty 相关联的 JAR 文件,以及 Gretty 的依赖项。注释 @GrabResolver(name='gretty', root='http://groovypp.artifactoryonline.com/groovypp/libs-releases-local') 表示 Grape 在何处可以发现所需的文件。

Grape 可能看起来简单,但是并不意味着它不适合生产。事实上,Grape 对所需依赖项的自动下载与 Maven 没有任何不同。只是 Grape 在运行时(首次运行应用程序时)下载,而 Maven 在构建时下载所需的依赖项。如果 Grape 可以在本地找到所需的依赖项,则不需要再进行下载。所需的 JAR 文件被自动放置在应用程序的类路径中。因此,您只需要为首次 运行某个已配置的 Grape 应用程序支付性能成本。当然,也会在您更改指定依赖项的所需版本时受到一个小的性能影响。

Gretty 适合 Magnus

希望到目前为止您已经发现 Gretty 是简单的,这就更易于进行非常快速的开发。此外,Gretty(或与此类似的框架)特别适合于 Magnus(HTTP 端点数据监听)这样的应用程序。那么让我们看看当完全使用一个 Gretty 编写的更轻量的应用程序来替换一个相对轻量的框架(比如 Play 或 Grails)时将会发生什么。

对于 Magnus 的具体实现,我将使用 Morphia 和 MongoHQ,您可以回顾我的 Amazon Elastic Beanstalk 简介。为了利用具有新配置的 Groovy 的 Grape 实用工具,我需要将清单 5 中的注释添加到服务器中:


清单 5. 添加 Morphia 及其依赖项
				
@GrabResolver(name='morphia', root='http://morphia.googlecode.com/svn/mavenrepo/')
@Grab(group='com.google.code.morphia', artifactId='morphia', module="morphia", 
  version='0.99')

我的 Morphia 类与 Magnus 的早期具体实现中的一样:我有一个 Account 和一个 Location。 在此端点中,我只简单地更新了某个给定帐户的位置。因为 Morphia 的客户端会将 JSON 文档发送至 Gretty 端点,我还要使用 Jackson(一个非常好的处理 JSON 的框架),它已经是 Gretty 的一部分。得益于 Grape 传递依赖的处理,现在我可以访问用于解析传入的 JSON 文档并将其转换为一个简单的 Java Map 所需要的一切。


清单 6. 在 Gretty 中更新位置
				
def server = new GrettyServer().localAddress(new InetSocketAddress("localhost", 8080)).
 "/location/:account" {
  put {
    def jacksonMapper = new ObjectMapper()
    def json = jacksonMapper.readValue(request.contentText, Map.class)
    def formatter = new SimpleDateFormat("dd-MM-yyyy HH:mm")
    def dt = formatter.parse(json['timestamp'])
    def res = [:]
    try{
      new Location(request.parameters['account'], dt, json['latitude'].doubleValue() , 
          json['longitude'].doubleValue() ).save()
	  res['status'] = 'success'
    }catch(exp){
      res['status'] = "error ${exp.message}"
    }
    response.json = jacksonMapper.writeValueAsString(res)
  }
}
server.start ()

正如您在 清单 6 中所看到的,创建了一个传入 JSON 文档的 Map(称为 json),然后通过清单 7 中的 Location 类相应地将其插入 MongoDB:


清单 7. 创建位置文档 Gretty redux
				
import com.google.code.morphia.annotations.Entity

@Entity(value = "locations", noClassnameStored = true)
class Location extends AbstractModel {
 String accountId
 double latitude
 double longitude
 Date timestamp

 public Location(String accountId, Date timestamp, double lat, double lon) {
  this.accountId = accountId
  this.timestamp = timestamp
  this.latitude = lat
  this.longitude = lon
 }
}

另外,Location 有一个 Groovy 超类,如清单 8 所示:


清单 8. Location 的基类
import com.google.code.morphia.Morphia
import com.google.code.morphia.annotations.Id
import com.mongodb.Mongo
import org.bson.types.ObjectId

abstract class AbstractModel {
  @Id
  private ObjectId id;

  def save() throws Exception {
    def mongo = new Mongo("fame.mongohq.com", 32422)
    def datastore = new Morphia().createDatastore(mongo, "xxxx", 
      "xxxx", "xxxx".toCharArray())
    datastore.save(this)
    return this.id
  }
}

您可能记得出自 “Climb the Elastic Beanstalk” 中清单 3 的代码。为了 Gretty 的实现,我所做的惟一更改是将实际的文件名从 Location.java 改为 Location.groovy,这意味着在激活服务器之前我不需要对其进行编译。我还添加了一个基类。通过从 URI 获得的传入参数 account 将位置与某个帐户相关联。

然后用 JSON 发送一个表示成功的响应。如果有错误,会产生另一个响应。

结束语:Gretty 已就绪

Gretty 是极其轻量级的。没有嵌入的 ORM 框架。除了简单的模板之外,没有强大的视图框架,但是插入一些其他的框架是完全可行的。所有这些是否意味着 Gretty 不适合日常使用?缺少测试框架是否也有同样的意思?答案是否定的:首先,Gretty 构建于 Netty 深受认可的代码之上,所以您大可放心。其次,您可以对 Gretty 进行自动或非自动的测试,就像您对任何其他 Web 端点所进行的测试一样。事实上,如果您想要了解 Gretty 是如何进行测试,请查看 Gretty 的源代码。Gretty 源代码中有大量的测试!

Gretty 与现代的全堆栈 Web 框架相对立,正是因为有时您不需要整个堆栈。如果您发现使用像 Gretty 这样的框架做了太多的工作,那么您可能最好使用许多全堆栈、存档完好的 Java Web 框架中的一个。同样地,如果您想要知道为什么需要整个堆栈来处理 Web 服务请求和响应,那么 Gretty 可能正是您所需要的。

举报
IBMdW
发帖于6年前 1回/1K+阅
顶部