OTS构建SpringMVC REST风格的准实时系统
一、简介
利用OTS为系统提供准实时系统交易流水持久化和离线批量数据存储服务。各系统的支撑系统,所有信息都会调用该服务,访问量大,存储和访问速度要求快,故服务的构建采用REST风格(SpringMVC框架天然的支持REST风格请求),对于准实时请求的响应和数据存储,采用NoSQL数据存储,即OTS。
在网站访问量还不大的情况下,单个数据库完全可以应付。随着访问量的上升,几乎大部分使用MySQL架构的网站在数据库上都开始出现了性能问题,web程序不再仅仅专注在功能上,同时也在追求性能。RDS for MySQL会从主从读写分离、分库分表(DRDS)多方面解决高并发的问题。大数据量高并发环境下的MySQL应用开发越来越复杂,分表分库的规则把握都是需要经验的。虽然有像淘宝这样技术实力强大的公司开发了透明的中间件层来屏蔽开发者的复杂性,但是避免不了整个架构的复杂性。
MySQL数据库也经常存储一些大文本字段,导致数据库表非常的大,在做数据库恢复的时候就导致非常的慢,不容易快速恢复数据库。比如1000万4KB大小的文本就接近40GB的大小,如果能把这些数据从MySQL省去,MySQL将变得非常的小。关系数据库很强大,但是它并不能很好的应付所有的应用场景。MySQL的扩展性差(需要复杂的技术来实现),大数据下IO压力大,表结构更改困难,正是当前使用MySQL的开发人员面临的问题。
而采用NoSQL可以避免如上的细节问题,NoSQL的优势:
NoSQL数据库种类繁多,但是一个共同的特点都是去掉关系数据库的关系型特性。数据之间无关系,这样就非常容易扩展。也无形之间,在架构的层面上带来了可扩展的能力。
NoSQL数据库都具有非常高的读写性能,尤其在大数据量下,同样表现优秀。这得益于它的无关系性,数据库的结构简单。
NoSQL无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式。而在关系数据库里,增删字段是一件非常麻烦的事情。如果是非常大数据量的表,增加字段简直就是一个噩梦。这点在大数据量的web2.0时代尤其明显。
阿里云表格存储(TableStore)OTS是 NoSQL 数据库的一种,提供海量 NoSQL 数据存储,支持 schemafree 的数据模型,提供单行级别的事务。服务端自动对数据进行分区和负载均衡,让单表数据从 GB 到 TB 再到 PB,访问并发从 0 至百万都无需繁琐的扩容流程,写性能在 TB 及 PB 级数据规模都能保持在单个毫秒,读性能只依赖结果数据集,而不受数据量的影响。所以相比 OLTP(联机事务处理)场景,表格存储更适用于 Web 规模级应用程序,包括社交网络、游戏、媒体共享和 IoT(物联网)、日志监控等场景。
二、框架搭建
1、浏览器发送rest请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE html>
< html xmlns = "http://www.w3.org/1999/xhtml" >
<!-- Head -->
< head >
< meta charset = "utf-8" />
< title >SpringMVC - Rest</ title >
< meta name = "description" content = "Dashboard" />
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" />
< meta http-equiv = "X-UA-Compatible" content = "IE=edge" />
< meta http-equiv = "Content-Type" content = "text/html; charset=utf-8" />
< link rel = "shortcut icon" href = "assets/img/favicon.png"
type = "image/x-icon" >
</ head >
<!-- /Head -->
<!-- Body -->
< body >
<!-- Page Body -->
< div class = "page-body" >
< div class = "row" >
< div class = "col-lg-12 col-sm-12 col-xs-12" >
< div class = "widget flat radius-bordered" >
< div class = "widget-header bordered-bottom bordered-themeprimary" >
< i class = "widget-icon fa fa-text-width" ></ i > < span
class = "widget-caption" >OTS构建SpringMVC REST风格的准实时系统</ span >
< div class = "widget-buttons" >
< a href = "#" data-action = "refresh" > < i class = "fa fa-undo" ></ i >
</ a > < a href = "#" data-toggle = "config" > < i class = "fa fa-cog" ></ i >
</ a > < a href = "#" data-toggle = "maximize" > < i class = "fa fa-expand" ></ i >
</ a > < a href = "#" data-toggle = "collapse" > < i class = "fa fa-minus" ></ i >
</ a > < a href = "#" data-toggle = "dispose" > < i class = "fa fa-times" ></ i >
</ a >
</ div >
<!--Widget Buttons-->
</ div >
<!--Widget Header-->
< div class = "widget-body no-padding" >
< div class = "widget-main" >
< table class = "table" >
< thead >
< tr >
< th width = "30%" >返回类型</ th >
< th >请求示例</ th >
</ tr >
</ thead >
< tbody >
< tr >
< td >Web Page</ td >
< td >
< h5 >
查询用户name为
< a href = "./getPageResult/许士" >许士</ a > 的记录.
</ h5 >
< h5 >
查询用户name为
< a href = "./getPageResult/柯思" >柯思</ a > 的记录.
</ h5 >
</ td >
</ tr >
< tr >
< td >JSON</ td >
< td >
< h5 >
查询用户name为
< a href = "./getJsonResult/许士" >许士</ a > 的记录.
</ h5 >
< h5 >
查询用户name为
< a href = "./getJsonResult/柯思" >柯思</ a > 的记录.
</ h5 >
</ td >
</ tr >
</ tbody >
</ table >
</ div >
<!--Widget Main Container-->
</ div >
<!--Widget Body-->
</ div >
<!--Widget-->
</ div >
</ div >
</ div >
</ body >
<!-- /Body -->
</ html >
|
2、Controller层映射对应方法
返回界面:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
package com.zt.mvc.web.controller;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.zt.mvc.web.model.User;
import com.zt.ots.OTSOperation;
@Controller
@RequestMapping ( "/getPageResult" )
class WebController {
/**
* 查询name=传入值的OTS记录
* @param name
* @param model
* @return
*/
@RequestMapping (value = "/{name}" , method = RequestMethod.GET)
public String getName( @PathVariable String name, ModelMap model) {
// 调用OTS查询功能
List<User> users = OTSOperation.getRowByFilter(name);
if (users != null && users.size() > 0 )
model.addAttribute( "user" , users.get( 0 ));
else
model.addAttribute( "user" , new User());
return "result" ;
}
}
|
返回JSON:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
package com.zt.mvc.web.controller;
import java.util.List;
import org.slf4j.Logger;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ExceptionHandler;
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.ResponseBody;
import com.zt.mvc.web.error.ErrorHolder;
import com.zt.mvc.web.exception.ResourceNFException;
import com.zt.mvc.web.model.User;
import com.zt.ots.OTSOperation;
@Controller
@RequestMapping ( "/getJsonResult" )
class JSonController {
/**
* 根据name查询,返回user对象
* @param name
* @param model
* @return
* @throws ResourceNFException
*/
@RequestMapping (value = "/{name}" , method = RequestMethod.GET)
@ResponseBody
public User getName( @PathVariable String name, ModelMap model) throws ResourceNFException {
System.out.println( "Name: " + name);
List<User> users = OTSOperation.getRowByFilter(name);
if (users != null && users.size() > 0 )
return users.get( 0 );
else
return new User();
}
@ExceptionHandler
@ResponseBody
public ResponseEntity<ErrorHolder> handle(ResourceNFException e) {
Logger logger = null ;
logger.warn( "The resource was not found" , e);
return new ResponseEntity<ErrorHolder>( new ErrorHolder( "The resource was not found" ),
HttpStatus.NOT_FOUND);
}
}
|
3、OTS过滤器查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
|
package com.zt.ots;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import com.aliyun.openservices.ots.ClientException;
import com.aliyun.openservices.ots.OTSClient;
import com.aliyun.openservices.ots.OTSException;
import com.aliyun.openservices.ots.model.ColumnValue;
import com.aliyun.openservices.ots.model.PrimaryKeyValue;
import com.aliyun.openservices.ots.model.RangeIteratorParameter;
import com.aliyun.openservices.ots.model.Row;
import com.aliyun.openservices.ots.model.RowPrimaryKey;
import com.aliyun.openservices.ots.model.condition.RelationalCondition;
import com.zt.mvc.web.model.User;
/**
* @author wanglu
* 描述:OTS 操作类
*
*/
public class OTSOperation {
private static final String COLUMN_GID_NAME = "gid" ;
private static final String COLUMN_UID_NAME = "uid" ;
private static final String COLUMN_NAME_NAME = "name" ;
private static final String COLUMN_ADDRESS_NAME = "address" ;
private static final String COLUMN_AGE_NAME = "age" ;
private static final String COLUMN_MOBILE_NAME = "mobile" ;
/**
* 使用过滤功能读取一行数据
* @param gid
* @param uid
*/
public static List<User> getRowByFilter(String name) {
final String endPoint = "http://cloud-report.cn-shanghai-zty-d01.ots.dcp.dev.ect-it.com" ;
final String accessId = "<access_key_id>" ;
final String accessKey = "<access_key_sceret>" ;
final String instanceName = "cloud-report" ; // 仅做测试的project
OTSClient client = new OTSClient(endPoint, accessId, accessKey, instanceName);
final String tableName = "User_Info" ;
try {
// 查询范围为:start primary key = (gid=0, INF_MIN)
// end primary key = (gid=500, INF_MAX)
// 且满足条件为:name == '方法输入name值'的所有行。
RangeIteratorParameter param = new RangeIteratorParameter(tableName);
RowPrimaryKey startPk = new RowPrimaryKey();
startPk.addPrimaryKeyColumn(COLUMN_GID_NAME, PrimaryKeyValue.fromLong( 0 ));
startPk.addPrimaryKeyColumn(COLUMN_UID_NAME, PrimaryKeyValue.INF_MIN);
RowPrimaryKey endPk = new RowPrimaryKey();
endPk.addPrimaryKeyColumn(COLUMN_GID_NAME, PrimaryKeyValue.fromLong( 500 ));
endPk.addPrimaryKeyColumn(COLUMN_UID_NAME, PrimaryKeyValue.INF_MAX);
param.setInclusiveStartPrimaryKey(startPk);
param.setExclusiveEndPrimaryKey(endPk);
RelationalCondition filter = new RelationalCondition(COLUMN_NAME_NAME, RelationalCondition.CompareOperator.EQUAL, ColumnValue.fromString(name));
param.setFilter(filter);
Iterator<Row> rowIter = client.createRangeIterator(param);
int totalRows = 0 ;
List<User> result = new LinkedList<User>();
User user;
while (rowIter.hasNext()) {
Row row = rowIter.next();
user = new User();
user.setGid(row.getColumns().get(COLUMN_GID_NAME).toString());
user.setUid(row.getColumns().get(COLUMN_UID_NAME).toString());
user.setName(row.getColumns().get(COLUMN_NAME_NAME).toString());
user.setAddress(row.getColumns().get(COLUMN_ADDRESS_NAME).toString());
user.setAge(row.getColumns().get(COLUMN_AGE_NAME).toString());
user.setMobile(row.getColumns().get(COLUMN_MOBILE_NAME).toString());
result.add(user);
totalRows++;
System.out.println(row);
}
System.out.println( "Total rows read: " + totalRows);
// 如果没有抛出异常,则说明成功
System.out.println( "Get row succeeded." );
return result;
} catch (ClientException ex) {
// 如果抛出客户端异常,则说明参数等有误
System.out.println( "Get row failed." );
} catch (OTSException ex) {
// 如果抛出服务器端异常,则说明请求失败
System.out.println( "Get row failed." );
} finally {
client.shutdown();
}
return null ;
}
}
|
4、查询结果返回前端显示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
<%@ page language= "java" import = "java.util.*" pageEncoding= "UTF-8" %>
<!DOCTYPE html>
<html xmlns= "http://www.w3.org/1999/xhtml" >
<!-- Head -->
<head>
<meta charset= "utf-8" />
<title>SpringMVC - Rest</title>
<meta name= "description" content= "Dashboard" />
<meta name= "viewport" content= "width=device-width, initial-scale=1.0" />
<meta http-equiv= "X-UA-Compatible" content= "IE=edge" />
<meta http-equiv= "Content-Type" content= "text/html; charset=utf-8" />
<link rel= "shortcut icon" href= "../assets/img/favicon.png"
type= "image/x-icon" >
</head>
<body>
<!-- Page Body -->
<div class = "page-body" >
<!-- Stacked To horizontal -->
<h5 class = "row-title before-blue" >Rest请求结果:</h5>
<div class = "grid-example" >
<div class = "row" >
<div class = "col-md-3" >Name - ${user.name}</div>
<div class = "col-md-3" >Address - ${user.address}</div>
<div class = "col-md-3" >Age - ${user.age}</div>
<div class = "col-md-3" >Mobile - ${user.mobile}</div>
</div>
</div>
</div>
</body>
<!-- /Body -->
</html>
|
三、示例运行
1、首界面
将项目部署到Tomcat中,启动Tomcat,浏览器中输入地址:localhost:8080/rest-springmvc-ots/,回车
2、返回结果为web page的请求
在返回类型为web page中,点击‘许士’超链接,经过OTS准实时查询,观察到浏览器的链接变为:localhost:8080/rest-springmvc-ots/getPageResult/许士,该rest请求表示发送查询name为‘许士’的请求,返回OTS准实时查询结果。当然,不过不通过超链接,直接在浏览器窗口输入rest请求localhost:8080/rest-springmvc-ots/getPageResult/许士,也是可以的。
再如,查询name为‘柯思’的记录:localhost:8080/rest-springmvc-ots/getPageResult/柯思
3、返回结果为json的请求
在返回类型为json时,点击首页中的超链接,或者发送rest请求,即可得到OTS返回结果为JSON格式的结果:localhost:8080/rest-springmvc-ots/getJsonResult/许士:
再如,查询name为‘柯思’的记录:localhost:8080/rest-springmvc-ots/getJsonResult/柯思
四、开发建议
开发参考:OTS 开发参考 Git:https://github.com/aliyun/aliyun-tablestore-java-sdk
开发参考:SpringMVC REST开发参考:http://jinnianshilongnian.iteye.com/blog/1996071
使用参考:Gogs:https://www.oschina.net/p/gogs
OTS构建SpringMVC REST风格的准实时系统相关推荐
- 快速构建Windows 8风格应用6-GridView数据控件
原文:快速构建Windows 8风格应用6-GridView数据控件 本篇博文主要介绍什么是GridView数据控件.如何构建常用的GridView数据呈现样式. 什么是GridView数据控件? G ...
- 《快速构建Windows 8风格应用》系列文章汇总
<快速构建Windows 8风格应用>系列的文章基于XAML+C#进行编写,汇总了Windows 8 Store应用开发过程中常用的开发技术点. 对于我个人来说,将自己平时学习和开发过程中 ...
- 快速构建Windows 8风格应用13-SearchContract构建
本篇博文主要介绍如何在应用中构建SearchContract,相应的原理已经在博文<快速构建Windows 8风格应用12-SearchContract概述及原理>中阐述清楚. 如何在应用 ...
- 快速构建Windows 8风格应用22-MessageDialog
快速构建Windows 8风格应用22-MessageDialog 原文:快速构建Windows 8风格应用22-MessageDialog 本篇博文主要介绍MessageDialog概述.Messa ...
- 快速构建Windows 8风格应用33-构建锁屏提醒
快速构建Windows 8风格应用33-构建锁屏提醒 原文:快速构建Windows 8风格应用33-构建锁屏提醒 引言 Windows Phone(8&7.5)和Windows 8引入了锁屏概 ...
- maven 构建 springmvc + spring security 权限控制示例
2019独角兽企业重金招聘Python工程师标准>>> maven 构建 springmvc + spring security 权限控制示例. 介绍 :Spring Securit ...
- SpringMVC+Json构建基于Restful风格的应用
为什么80%的码农都做不了架构师?>>> https://git.oschina.net/zjg23/SpringMVCRestful.git 转载于:https://my.o ...
- 快速构建Windows 8风格应用32-构建辅助磁贴
引言 Windows Phone中,我们开发者可能会开发的一个功能点是将数据列表中某一项"Pin To Start(固定到开始屏幕)",大家都知道这种固定到开始屏幕的磁贴叫做辅助磁 ...
- Flask 系列之 构建 Swagger UI 风格的 WebAPI
说明 操作系统:Windows 10 Python 版本:3.7x 虚拟环境管理器:virtualenv 代码编辑器:VS Code 实验 环境初始化 # 创建项目目录 mkdir helloworl ...
最新文章
- q160问题,www.q160.com,ie被篡改
- [gic]-ARM gicv3/gicv2的总结和介绍-PPT
- Java工作笔记-webService发布时通用的4个注解
- C导出接口与C++导出接口对比
- HDOJ 1257 (最长字序列问题)
- 如何在Windows平台下可以方便获取到android的源码?
- mysql_error 1030
- DimDate populate data
- 训练集和测试集样本分布一致性的判断方法
- 4、BFS算法套路框架——Go语言版
- PS安装失败解决方法
- 1534 棋子游戏(博弈论)
- JavaScript - 获取、修改 title 元素的内容
- word排版技巧:如何撤销删除自动编号
- 多数投票算法(Boyer-Moore Algorithm)
- seo和sem有什么关系
- 线性回归算法梳理——Test1
- 数据分析课设(SPSS,EVIEWS,R)【理论】
- Linux常用命令——ss命令
- 计算机网络实验以太网帧分析,实验二 用Ethereal捕获并分析以太网帧格式