OTS构建SpringMVC REST风格的准实时系统

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 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>
<!-- 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/,回车

111.png?version=1&modificationDate=14884

2、返回结果为web page的请求

    在返回类型为web page中,点击‘许士’超链接,经过OTS准实时查询,观察到浏览器的链接变为:localhost:8080/rest-springmvc-ots/getPageResult/许士,该rest请求表示发送查询name为‘许士’的请求,返回OTS准实时查询结果。当然,不过不通过超链接,直接在浏览器窗口输入rest请求localhost:8080/rest-springmvc-ots/getPageResult/许士,也是可以的。

2222.png?version=1&modificationDate=1488

    再如,查询name为‘柯思’的记录:localhost:8080/rest-springmvc-ots/getPageResult/柯思

333.png?version=1&modificationDate=14884

 

3、返回结果为json的请求

    在返回类型为json时,点击首页中的超链接,或者发送rest请求,即可得到OTS返回结果为JSON格式的结果:localhost:8080/rest-springmvc-ots/getJsonResult/许士:

444.png?version=1&modificationDate=14884

    再如,查询name为‘柯思’的记录:localhost:8080/rest-springmvc-ots/getJsonResult/柯思

555.png?version=1&modificationDate=14884

 

 

四、开发建议

     开发参考: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

相关实践学习
阿里云表格存储使用教程
表格存储(Table Store)是构建在阿里云飞天分布式系统之上的分布式NoSQL数据存储服务,根据99.99%的高可用以及11个9的数据可靠性的标准设计。表格存储通过数据分片和负载均衡技术,实现数据规模与访问并发上的无缝扩展,提供海量结构化数据的存储和实时访问。 产品详情:https://www.aliyun.com/product/ots
目录
相关文章
|
存储 分布式计算 运维
塑云科技:性能突破,基于KafKa+OTS+MaxCompute 完成了一次物联网系统技术重构
创业团队,专注于氢能燃料电池生态链的运营支撑,当前主要的业务组成为新能源车整车实时运营监控分析,加氢站实时运营监控分析,车辆安全运营支撑。
2072 0
塑云科技:性能突破,基于KafKa+OTS+MaxCompute 完成了一次物联网系统技术重构
|
存储 NoSQL 开发工具
基于Tablestore多元索引打造亿量级店铺搜索系统
如何使用TableStore打造店铺搜索系统
8645 0
|
存储 分布式计算 监控
塑云科技:性能突破,基于KafKa+OTS+MaxCompute 完成了一次物联网系统技术重构
塑云科技:性能突破,基于KafKa+OTS+MaxCompute 完成了一次物联网系统技术重构 背景:创业团队,专注于氢能燃料电池生态链的运营支撑,当前主要的业务组成为新能源车整车实时运营监控分析,加氢站实时运营监控分析,车辆安全运营支撑。
13683 0
|
Web App开发 存储 分布式计算
基于TableStore/MaxCompute的数据采集分析系统介绍
摘要 在互联网高度发达的今天,ipad、手机等智能终端设备随处可见,运行在其中的APP、网站也非常多,如何采集终端数据进行分析,提升软件的品质非常重要,例如PV/UV统计、用户行为数据统计与分析等。虽然场景简单,但是数据量大,对系统的吞吐量、实时性、分析能力、查询能力都有较高的要求,搭建起来并不容易。
4844 0
|
存储 NoSQL Java
TableStore Timeline:轻松构建千万级IM和Feed流系统
在文章《现代IM系统中消息推送和存储架构的实现》中介绍了一种适用于IM的消息存储和推送模型Timeline,在本篇文章中,会扩展Timeline模型到IM和Feed流系统中,并且提供成熟的LIB实现。用户基于TableStore-Timeline LIB可轻松实现千万级的IM和Feed流系统。
24932 1
|
存储 NoSQL 物联网
TableStore+OSS:物联网数据的备份系统实践
: TableStore是阿里云自研专业级分布式NoSQL数据库,OSS是阿里云的对象存储服务,本篇文章会介绍如何同步TableStore中的数据到OSS中,以便对冷数据做更长久时间的低成本存储。
4590 0
|
存储 弹性计算 NoSQL
TableStore+ Elasticsearch:海量图书信息全文检索系统实践
TableStore是阿里云自研专业级分布式NoSQL数据库,Elasticsearch是著名的开源搜索引擎,本篇文章会介绍如何同步TableStore中的数据到Elasticsearch中,以便对部分字段支持搜索功能。
7684 0
|
Web App开发 存储 数据采集
基于TableStore的数据采集分析系统介绍
摘要 在互联网高度发达的今天,ipad、手机等智能终端设备随处可见,运行在其中的APP、网站也非常多,如何采集终端数据进行分析,提升软件的品质非常重要,例如PV/UV统计、用户行为数据统计与分析等。虽然场景简单,但是数据量大,对系统的吞吐量、实时性、分析能力、查询能力都有较高的要求,搭建起来并不容易。
7612 0
|
8月前
|
存储 索引
表格存储根据多元索引查询条件直接更新数据
表格存储是否可以根据多元索引查询条件直接更新数据?
68 3
|
18天前
|
分布式计算 DataWorks API
DataWorks常见问题之按指定条件物理删除OTS中的数据失败如何解决
DataWorks是阿里云提供的一站式大数据开发与管理平台,支持数据集成、数据开发、数据治理等功能;在本汇总中,我们梳理了DataWorks产品在使用过程中经常遇到的问题及解答,以助用户在数据处理和分析工作中提高效率,降低难度。