1. 概述
Ratpack是一组基于JVM的库,专为当今的高性能实时应用程序而构建。它建立在嵌入式Netty事件驱动网络引擎之上,完全符合响应式设计模式。
在本文中,我们将学习如何使用Ratpack并使用它构建一个小型应用程序。
2. 为什么选择Ratpack
Ratpack的主要优点:
- 非常轻量、快速且可扩展
- 比其他框架(如DropWizard)消耗的内存更少;可以在这里找到一个有趣的基准比较结果
- 由于Ratpack是基于Netty构建的,因此它完全是事件驱动的,并且本质上是非阻塞的
- 它支持Guice依赖管理
- 与Spring Boot类似,Ratpack有自己的测试库,可以快速设置测试用例
3. 创建应用程序
为了了解Ratpack的工作原理,我们首先用它创建一个小型应用程序。
3.1 Maven依赖
首先,让我们在pom.xml中添加以下依赖:
<dependency>
<groupId>io.ratpack</groupId>
<artifactId>ratpack-core</artifactId>
<version>1.4.5</version>
</dependency>
<dependency>
<groupId>io.ratpack</groupId>
<artifactId>ratpack-test</artifactId>
<version>1.4.5</version>
</dependency>
你可以在Maven Central上查看最新版本。
请注意,尽管我们使用Maven作为构建系统,但根据Ratpack的建议,最好使用Gradle作为构建工具,因为Ratpack通过Ratpack的Gradle插件提供了一流的Gradle支持。
我们可以使用以下构建Gradle脚本:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "io.ratpack:ratpack-gradle:1.4.5"
}
}
apply plugin: "io.ratpack.ratpack-java"
repositories {
jcenter()
}
dependencies {
testCompile 'junit:junit:4.11'
runtime "org.slf4j:slf4j-simple:1.7.21"
}
test {
testLogging {
events 'started', 'passed'
}
}
3.2 构建应用程序
一旦我们的构建管理配置完毕,我们需要创建一个类来启动嵌入式Netty服务器并构建一个简单的上下文来处理默认请求:
public class Application {
public static void main(String[] args) throws Exception {
RatpackServer.start(server -> server.handlers(chain -> chain
.get(ctx -> ctx.render("Welcome to Tuyucheng ratpack!!!"))));
}
}
我们可以看到,通过使用RatpackServer,我们现在可以启动服务器(默认端口5050)。handlers()方法接收一个接收Chain对象的函数,该对象映射所有相应的传入请求。此“Handler链API”用于构建响应处理策略。
如果我们运行此代码片段并在浏览器中访问http://localhost:5050,将会显示“Welcome to Tuyucheng ratpack!!!”。
类似地,我们可以映射HTTP POST请求。
3.3 处理URL路径参数
在下一个示例中,我们需要在应用程序中捕获一些URL路径参数。在Ratpack中,我们使用PathTokens来捕获它们:
RatpackServer.start(server -> server
.handlers(chain -> chain
.get(":name", ctx -> ctx.render("Hello "
+ ctx.getPathTokens().get("name") + " !!!"))));
这里,我们映射了name URL参数,每当收到类似http://localhost:5050/John的请求时,响应将是“Hello John!!!”。
3.4. 使用/不使用过滤器修改请求/响应头
有时,我们需要根据需要修改内联HTTP响应标头,Ratpack具有MutableHeaders来自定义传出响应。
例如,我们需要更改响应中的以下标头:Access-Control-Allow-Origin、Accept-Language和Accept-Charset:
RatpackServer.start(server -> server.handlers(chain -> chain.all(ctx -> {
MutableHeaders headers = ctx.getResponse().getHeaders();
headers.set("Access-Control-Allow-Origin", "*");
headers.set("Accept-Language", "en-us");
headers.set("Accept-Charset", "UTF-8");
ctx.next();
}).get(":name", ctx -> ctx
.render("Hello " + ctx.getPathTokens().get("name") + "!!!"))));
通过使用MutableHeaders我们设置3个标头并将它们推送到Chain中。
同样,我们也可以检查传入的请求标头:
ctx.getRequest().getHeaders().get("//TODO")
通过创建过滤器也可以实现同样的效果,Ratpack有一个Handler接口,可以实现该接口来创建过滤器。它只有一个方法handle(),以当前Context作为参数:
public class RequestValidatorFilter implements Handler {
@Override
public void handle(Context ctx) throws Exception {
MutableHeaders headers = ctx.getResponse().getHeaders();
headers.set("Access-Control-Allow-Origin", "*");
ctx.next();
}
}
我们可以按照以下方式使用该过滤器:
RatpackServer.start(
server -> server.handlers(chain -> chain
.all(new RequestValidatorFilter())
.get(ctx -> ctx.render("Welcome to tuyucheng ratpack!!!"))));
}
3.5 JSON解析器
Ratpack内部使用faster-jackson进行JSON解析,我们可以使用Jackson模块将任何对象解析为JSON。
让我们创建一个用于解析的简单POJO类:
public class Employee {
private Long id;
private String title;
private String name;
// getters and setters
}
这里我们创建了一个名为Employee的简单POJO类,它有3个参数:id、title和name。现在我们将使用这个Employee对象转换成JSON,并在访问某个URL时返回相同的JSON:
List<Employee> employees = new ArrayList<Employee>();
employees.add(new Employee(1L, "Mr", "John Doe"));
employees.add(new Employee(2L, "Mr", "White Snow"));
RatpackServer.start(
server -> server.handlers(chain -> chain
.get("data/employees",
ctx -> ctx.render(Jackson.json(employees)))));
如我们所见,我们手动将两个Employee对象添加到列表中,并使用Jackson模块将它们解析为JSON。只要访问/data/employees URL,就会返回JSON对象。
这里要注意的是,我们根本没有使用ObjectMapper,因为Ratpack的Jackson模块会动态地完成必要的操作。
3.6 内存数据库
Ratpack对内存数据库提供一流的支持,它使用HikariCP进行JDBC连接池。为了使用它,我们需要在pom.xml中添加Ratpack的HikariCP模块依赖:
<dependency>
<groupId>io.ratpack</groupId>
<artifactId>ratpack-hikari</artifactId>
<version>1.4.5</version>
</dependency>
如果我们使用Gradle,则需要在Gradle构建文件中添加相同的内容:
compile ratpack.dependency('hikari')
现在,我们需要创建一个包含表DDL语句的SQL文件,以便在服务器启动并运行后立即创建表。我们将在src/main/resources目录中创建DDL.sql文件并在其中添加一些DDL语句。
由于我们使用的是H2数据库,因此我们也必须为其添加依赖。
现在,通过使用HikariModule,我们可以在运行时初始化数据库:
RatpackServer.start(
server -> server.registry(Guice.registry(bindings ->
bindings.module(HikariModule.class, config -> {
config.setDataSourceClassName("org.h2.jdbcx.JdbcDataSource");
config.addDataSourceProperty("URL",
"jdbc:h2:mem:tuyucheng;INIT=RUNSCRIPT FROM 'classpath:/DDL.sql'");
}))).handlers(...));
4. 测试
如前所述,Ratpack对JUnit测试用例提供了一流的支持,通过使用MainClassApplicationUnderTest,我们可以轻松创建测试用例并测试端点:
@RunWith(JUnit4.class)
public class ApplicationTest {
MainClassApplicationUnderTest appUnderTest = new MainClassApplicationUnderTest(Application.class);
@Test
public void givenDefaultUrl_getStaticText() {
assertEquals("Welcome to tuyucheng ratpack!!!", appUnderTest.getHttpClient().getText("/"));
}
@Test
public void givenDynamicUrl_getDynamicText() {
assertEquals("Hello dummybot!!!", appUnderTest.getHttpClient().getText("/dummybot"));
}
@Test
public void givenUrl_getListOfEmployee() throws JsonProcessingException {
List<Employee> employees = new ArrayList<Employee>();
ObjectMapper mapper = new ObjectMapper();
employees.add(new Employee(1L, "Mr", "John Doe"));
employees.add(new Employee(2L, "Mr", "White Snow"));
assertEquals(mapper.writeValueAsString(employees), appUnderTest.getHttpClient().getText("/data/employees"));
}
@After
public void shutdown() {
appUnderTest.close();
}
}
请注意,我们需要通过调用close()方法手动终止正在运行的MainClassApplicationUnderTest实例,因为它可能会不必要地阻塞JVM资源,这就是为什么我们使用@After注解在测试用例执行后强制终止实例。
5. 总结
在本文中,我们介绍了使用Ratpack的简单性。
Post Directory
