写点什么

使用 Spring Boot 构建 RESTful Web 服务以访问存储于 Aerospike 集群中的数据

  • 2014-02-20
  • 本文字数:8257 字

    阅读完需:约 27 分钟

Spring Boot 是对 Spring 快速入门的强大工具。Spring Boot 能够帮助你很容易地构建基于 Spring 的应用。

Aerospike 是分布式和可复制的内存数据库,不管使用 DRAM 还是原生的 flash/SSD,Aerospike 都进行了优化。

Aerospike 具有高可靠性并且遵循 ACID。开发人员能够在不停止数据库服务的情况下,很快地将数据库集群从两个节点扩展到二十个节点。

你所要构建的是什么

本文将会引领你使用 Spring Boot 创建一个简单的 RESTful Web 服务。

要构建的服务接受一个 HTTP GET 请求。它的响应是如下的 JSON:

复制代码
{"expiration":121023390,"bins":{"DISTANCE":2446,"DEST_CITY_NAME":"New
York","DEST":"JFK","YEAR":2012,"ORI_AIRPORT_ID":"14679","DEP_TIME":
"802","DAY_OF_MONTH":12,"DEST_STATE_ABR":"NY","ORIGIN":"SAN","FL_NUM"
:160,"CARRIER":"AA","ORI_STATE_ABR":"CA","FL_DATE":"2012/01/12",
"AIR_TIME":291,"ORI_CITY_NAME":"San Diego","ELAPSED_TIME":321,
"ARR_TIME":"1623","AIRLINE_ID":19805},"generation":1}

这里所使用的数据是商业上的飞行航班详情(包含在样例代码中,这是一个名为 flights_from.csv 的数据文件,它包含了大约一百万条航班信息)。

在产品化(或其他)环境中,还会有很多内置的特性添加到应用中以管理服务。这个功能来源于 Spring,参见 Spring 指导: Building a RESTful web service

你所需要的是什么

搭建工程

在构建应用的时候,你可以使用任何喜欢的构建系统,不过在这里提供了 Maven 的代码。如果你不熟悉 Maven 的话,请参考 Spring 指导: Building Java Projects with Maven

你还需要构建并安装 Aerospike 的 Java 客户端到本地 Maven 仓库之中。下载源码发布版本,将其进行进行 unzip/untar 并运行如下的 Maven 命令:

  • mvn install:install-file -Dfile=client/depends/gnu-crypto.jar -DgroupId=org.gnu -DartifactId=gnu-crypto -Dversion=2.0.1 -Dpackaging=jar
  • mvn clean
  • mvn package

创建目录结构

在你选择的工程之中,创建如下所示的子目录结构:

->src
->main
->java
->com
->aerospike
->client
->rest

创建 Maven 的 pom 文件

在工程的根目录下创建一个 maven 的 pom.xml,其代码如下:

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<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.aerospike</groupId>
<artifactId>aerospike-restful-example</artifactId>
<version>1.0.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>0.5.0.M4</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Aerospike client. -->
<dependency>
<groupId>com.aerospike</groupId>
<artifactId>aerospike-client</artifactId>
<version>3.0.9</version>
</dependency>
<!-- Apache command line parser. -->
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
<properties>
<start-class>com.aerospike.client.rest.AerospikeRESTfulService
</start-class>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>http://repo.spring.io/libs-snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>http://repo.spring.io/libs-snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>

乍看上去有些恐怖,但实际上并非如此。

创建一个 JSON 转换类

Aerospike API 会返回一个 Record 对象,它会包含记录的 generation、expiry 以及 bin 值。但是你想让这些值以 JSON 格式返回。要达到这一点,最简单的方式就是使用一个转换类(translator class)。

所创建的转换类代码如下所示。这是一个工具类,能够将 Aerospike Record 转换为 JSONObject。

复制代码
<span color="#408080">src/main/java/com/aerospike/client/rest/JSONRecord.java</span>
package com.aerospike.client.rest;
import java.util.Map;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import com.aerospike.client.Record;
/**
* JSONRecord is used to convert an Aerospike Record
* returned from the cluster to JSON format
*
*/
@SuppressWarnings("serial")
public class JSONRecord extends JSONObject {
@SuppressWarnings("unchecked")
public JSONRecord(Record record){
put("generation", record.generation);
put("expiration", record.expiration);
put("bins", new JSONObject(record.bins));
if (record.duplicates != null){
JSONArray duplicates = new JSONArray();
for (Map<String, Object> duplicate : record.duplicates){
duplicates.add(new JSONObject(duplicate));
}
put("duplicates", duplicates);
}
}
}

这个类并不复杂也很通用。你可能会希望为特定的记录指定使用你的 JSON 转换器。

创建资源控制器

在 Spring 中,REST 端点(endpoint)是 Spring MVC 控制器。如下的代码能够处理对 /as/{namespace}/{set}/getAll/1234 的 GET 请求,并会返回 key 为 1234 的航班记录,在这里{namespace}是针对 Aerospike 命名空间的路径变量,{set}是针对 Aerospike 集合的路径变量。

复制代码
<span color="#408080">src/main/java/com/aerospike/client/rest/RESTController.java</span>
package com.aerospike.client.rest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import com.aerospike.client.AerospikeClient;
import com.aerospike.client.Bin;
import com.aerospike.client.Key;
import com.aerospike.client.Record;
import com.aerospike.client.policy.Policy;
import com.aerospike.client.policy.WritePolicy;
@Controller
public class RESTController {
@Autowired
AerospikeClient client;
@RequestMapping(value="/as/{namespace}/{set}/getAll/{key}",
method=RequestMethod.GET)
public @ResponseBody JSONRecord getAll(@PathVariable
("namespace") String namespace,
@PathVariable("set") String set,
@PathVariable("key") String keyvalue) throws Exception {
Policy policy = new Policy();
Key key = new Key(namespace, set, keyvalue);
Record result = client.get(policy, key);
return new JSONRecord(result);
}
}

针对人类用户的控制器和针对 REST 端点控制器之间的区别在于响应体中要包含数据,在这个场景中也就是一个 JSON 对象,它代表了从 Aerospike 读取到的记录。

@ResponseBody 注解会告知 Spring MVC 将返回的对象写入到响应体之中。

创建可执行的主类

现在要实现主方法来创建 Spring MVC 控制器,最简单的方式就是使用 SpringApplication 帮助类。

复制代码
<span color="#408080">src/main/java/com/aerospike/client/rest/AerospikeRESTfulService.java</span>
package com.aerospike.client.rest;
import java.util.Properties;
import javax.servlet.MultipartConfigElement;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import com.aerospike.client.AerospikeClient;
import com.aerospike.client.AerospikeException;
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class AerospikeRESTfulService {
@Bean
public AerospikeClient asClient() throws AerospikeException {
Properties as = System.getProperties();
return new AerospikeClient(as.getProperty("seedHost"),
Integer.parseInt(as.getProperty("port")));
}
@Bean
public MultipartConfigElement multipartConfigElement() {
return new MultipartConfigElement("");
}
public static void main(String[] args) throws ParseException {
Options options = new Options();
options.addOption("h", "host", true,
"Server hostname (default: localhost)");
options.addOption("p", "port", true, "Server port (default: 3000)");
// parse the command line args
CommandLineParser parser = new PosixParser();
CommandLine cl = parser.parse(options, args, false);
// set properties
Properties as = System.getProperties();
String host = cl.getOptionValue("h", "localhost");
as.put("seedHost", host);
String portString = cl.getOptionValue("p", "3000");
as.put("port", portString);
// start app
SpringApplication.run(AerospikeRESTfulService.class, args);
}
}

这里添加了 @EnableAutoConfiguration 注解:它会对一些内容进行默认的加载(如嵌入式的 servlet 容器),这取决于类路径的内容以及其他的一些事情。

它还使用了 @ComponentScan 注解,这个注解会告诉 Spring 扫描 rest 包来查找控制器(以及其他有注解的组件类)。

最后,这个类还使用了 @Configuration 注解。它允许你将 AerospikeClient 实例配置为一个 Spring 的 bean。

这里还定义了一个 MultipartConfigElement bean。它能够让你使用这个服务处理 POST 操作。

主方法中大部分的主体内容都是读取命令行参数以及系统属性,以便指定 Aerospike 集群的 seed 主机和端口。

非常简单!

上传数据

你可能希望往这个服务中上传数据。要做到这一点的话,我们需要为 RESTController 类添加一个额外的方法来处理上传的文件。在这个例子中,这会是包含航行记录的 CSV 文件。

复制代码
<span color="#408080">src/main/java/com/aerospike/client/rest/RESTController.java</span>
@Controller
public class RESTController {
. . . (code omitted) . . .
/*
* CSV flights file upload
*/
@RequestMapping(value="/uploadFlights", method=RequestMethod.GET)
public @ResponseBody String provideUploadInfo() {
return "You can upload a file by posting to this same URL.";
}
@RequestMapping(value="/uploadFlights", method=RequestMethod.POST)
public @ResponseBody String handleFileUpload(@RequestParam("name") String name,
@RequestParam("file") MultipartFile file){
if (!file.isEmpty()) {
try {
WritePolicy wp = new WritePolicy();
String line = "";
BufferedReader br = new BufferedReader(new
InputStreamReader(file.getInputStream()));
while ((line = br.readLine()) != null) {
// use comma as separator
String[] flight = line.split(",");
/*
* write the record to Aerospike
* NOTE: Bin names must not exceed 14 characters
*/
client.put(wp,
new Key("test", "flights",flight[0].trim() ),
new Bin("YEAR", Integer.parseInt(flight[1].trim())),
new Bin("DAY_OF_MONTH", Integer.parseInt(flight[2].trim())),
new Bin("FL_DATE", flight[3].trim()),
new Bin("AIRLINE_ID", Integer.parseInt(flight[4].trim())),
new Bin("CARRIER", flight[5].trim()),
new Bin("FL_NUM", Integer.parseInt(flight[6].trim())),
new Bin("ORI_AIRPORT_ID", Integer.parseInt(flight[7].trim())),
new Bin("ORIGIN", flight[8].trim()),
new Bin("ORI_CITY_NAME", flight[9].trim()),
new Bin("ORI_STATE_ABR", flight[10].trim()),
new Bin("DEST", flight[11].trim()),
new Bin("DEST_CITY_NAME", flight[12].trim()),
new Bin("DEST_STATE_ABR", flight[13].trim()),
new Bin("DEP_TIME", Integer.parseInt(flight[14].trim())),
new Bin("ARR_TIME", Integer.parseInt(flight[15].trim())),
new Bin("ELAPSED_TIME", Integer.parseInt(flight[16].trim())),
new Bin("AIR_TIME", Integer.parseInt(flight[17].trim())),
new Bin("DISTANCE", Integer.parseInt(flight[18].trim()))
);
System.out.println("Flight [ID= " + flight[0]
+ " , year=" + flight[1]
+ " , DAY_OF_MONTH=" + flight[2]
+ " , FL_DATE=" + flight[3]
+ " , AIRLINE_ID=" + flight[4]
+ " , CARRIER=" + flight[5]
+ " , FL_NUM=" + flight[6]
+ " , ORIGIN_AIRPORT_ID=" + flight[7]
+ "]");
}
br.close();
return "You successfully uploaded " + name;
} catch (Exception e) {
return "You failed to upload " + name + " => " + e.getMessage();
}
} else {
return "You failed to upload " + name +
" because the file was empty.";
}
}
}

新方法 handleFileUpload() 响应 POST 请求并且会读取上传的流,每次读取一行。每一行解析后,会构建一个 Key 对象和多个 Bin 对象,据此来形成 Aerospike 记录。最后,调用 Aerospike 的 put() 方法,将记录存储到 Aerospike 集群之中。

另外一个新方法 provideUploadInfo() 响应 GET 请求,并返回一条信息来表明允许进行上传。

上传的客户端应用

上传可以通过任何你希望的方式来实现。不过,你可以使用下面这个单独的 Java 类将数据上传到服务上。

复制代码
<span color="#408080">src/test/java/com.aerospike.client.rest/FlightsUploader.java</span>
package com.aerospike.client.rest;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.io.FileSystemResource;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
public class FilghtsUploader {
private static final String TEST_FILE = "flights_from.csv";
@Before
public void setUp() throws Exception {
}
@Test
public void upload() {
RestTemplate template = new RestTemplate();
MultiValueMap<String, Object> parts = new LinkedMultiValueMap
<String, Object>();
parts.add("name", TEST_FILE);
parts.add("file", new FileSystemResource(TEST_FILE));
String response = template.postForObject
("<a href="http://localhost:8080/uploadFlights">http://localhost:8080/uploadFlights</a>",parts, String.class);
System.out.println(response);
}
}

航班数据

这是来自 2012 年的真实数据,包括了大约一百万条的记录,所以请注意它需要几分钟的时间才能完成上传。

构建并运行服务

Maven 的 pom.xml 会将服务打包为一个单独的 jar 文件。使用如下的命令:

mvn clean package

这样会生成独立的 web 服务应用,它会打包为一个可运行的 jar 文件,位于 target 子目录之中。这个 jar 文件中包含了一个 Tomcat 的实例,所以你可以直接运行这个 jar 文件,而没有必要将其安装到应用服务器之中。

java -jar aerospike-restful-example-1.0.0.jar

总结

恭喜你!你现在已经使用 Spring 开发了一个简单的 RESTful 服务,并且连接到了 Aerospike 集群之中。

完整的样例代码

样例代码

设计中的考量

目前,访问控制是通过应用来处理的,并不是通过数据库。因为认证过程会拖慢数据库的速度,实际上,所有的NoSQL 数据库均不支持这种功能。我们的大多数客户更关注于提升的速度,而不是集成的认证特性。

另外一个要求的通用特性就是两个不同数据集之间的连接(join)。对于所有的分布式数据库来讲,这都是一个挑战,因为要连接的数据是分布式的。在本例中,开发人员必须在应用中实现连接。

关于作者

Peter Milne是一位很有经验的 IT 专业人士,对于软件开发和产品的整个生命周期都有着丰富的经验。对于小型和大型的开发团队,他都具有技术技能和管理经验。Peter 最近以来在 Aerospike 担任高级解决方案架构师。在此之前,他在 MLC 担任高级分析师和编码人员,并且在 iTerative Consulting 担任过 CTO,在此期间,他构建了一个 Forte/UDS 到 Java 的转换工具,达到了 99.999% 准确率。Peter 在悉尼科技大学获得了分布式计算的理科硕士学位,并且具有多个直升机安全许可和证书。

原文英文链接: Building a RESTful Web Service with Spring Boot to Access Data in an Aerospike Cluster

2014-02-20 23:3013284

评论

发布
暂无评论
发现更多内容

「mac」JProfiler 14 激活版(功能强大的Java性能分析工具)

iMac小白

JProfiler 14激活版 JProfiler 14下载 JProfiler 14 mac JProfiler 14破解版

石原子科技亮相2023成都市信息领域新产品发布会

StoneDB

MySQL 数据库 HTAP StoneDB

选购护眼台灯,全网都没有说清一个关键点!——照度均匀度

电子信息发烧客

轻量级前端架构之:小程序技术

Speedoooo

小程序容器 小程序技术 小程序容器技术 微前端架构 轻量级前端架构

英伟达中国特供芯片是缩水版;华为 Mate60 Pro 国产零件价值占比 47%丨 RTE 开发者日报 Vol.84

声网

选购美国云服务器,轻松打造高效网络

一只扑棱蛾子

美国服务器 美国云服务器

软件测试/测试开发丨探索Python中的函数定义和调用

测试人

软件测试

守护 C 盘,Python 相关库设置

北桥苏

Python conda anconda

超级App的前端框架也可以足够轻量

FN0

前端框架 超级app

NLP技术如何为搜索引擎赋能

不在线第一只蜗牛

nlp NLP 大模型 技术 优化体系

数字马力面经和答案解析!社招岗

王磊

Java 面试 java面试

区块链开发:区块链软件开发包装相关解析

区块链软件开发推广运营

dapp开发 区块链开发 链游开发 NFT开发 公链开发

以程序员的身份,教您使用API接口获取虾皮商品详情

Noah

简单地聊一聊Spring Boot的构架

快乐非自愿限量之名

前端 spring-boot

mac磁盘清理必备:Magic Disk Cleaner破解最新版

iMac小白

中馥集团双11当日发货销售额突破1000万!

电子信息发烧客

华为音乐枫叶音乐会,倾耳聆听心动音乐故事

最新动态

新一代信息技术成为数字化转型满意度评价新要素

Geek_2d6073

Richardson Software RazorSQL (多功能SQL数据库编辑器)注册版下载

iMac小白

万字解析XML配置映射为BeanDefinition的源码

华为云开发者联盟

spring 开发 华为云 华为云开发者联盟

【Windows 开发环境配置——NVIDIA 篇】CUDA、cuDNN、TensorRT 三件套安装

快乐非自愿限量之名

windows 开发环境

「mac」KeyShot Pro功能强大的3D渲染和动画制作软件

iMac小白

KeyShot Pro下载 KeyShot Pro破解版

VideoProc Converter for Mac(多功能视频处理软件) v6.1中文激活版

mac

苹果mac Windows软件 视频处理软件 VideoProc Converter

一文解码语言模型:语言模型的原理、实战与评估

EquatorCoco

概念介绍 语言模型

八个开源免费的低代码/无代码后端项目

高端章鱼哥

开源 低代码 无代码

龙蜥社区联合浪潮信息发布《eBPF技术实践白皮书》(附下载链接)

OpenAnolis小助手

Linux 白皮书 ebpf 云栖大会 龙蜥社区

动作活体检测能力支持自定义扫描动作,开发者接入更高效

HarmonyOS SDK

HMS Core

解决室内种植最大弊端的是方法—植物生长灯

电子信息发烧客

JavaScript slice 方法使用指南

Liam

JavaScript 程序员 前端 前端开发 slice

11月创作挑战赛开启!新奖品、新标准~

Openlab_cosmoplat

使用Spring Boot构建RESTful Web服务以访问存储于Aerospike集群中的数据_语言 & 开发_Peter Milne_InfoQ精选文章