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

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
云原生大数据计算服务MaxCompute,500CU*H 100GB 3个月
表格存储 Tablestore,50GB 3个月
简介: 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>
<!-- 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
目录
相关文章
|
6天前
|
XML JSON Java
SpringMVC与REST相结合实现RESTful风格
SpringMVC与REST相结合实现RESTful风格
44 0
|
3月前
|
XML 缓存 API
分布式系统核心:REST风格的架构,REST成熟度模型及REST API管理
正如前文所述,正确、完整地使用REST是困难的,关键在于RoyFielding所定义的REST只是一种架构风格,它并不是规范,所以也就缺乏可以直接参考的依据。好在Leonard Richardson补充了这方面的不足。
|
3月前
|
Oracle 关系型数据库 MySQL
超简单的C#可配置可扩展基础框架示例
超简单的C#可配置可扩展基础框架示例
|
4月前
|
Java 测试技术 API
探索Swagger:简化API文档管理与测试的利器
在现代的应用开发中,API(Application Programming Interface)是不可或缺的一部分,用于实现不同系统之间的通信和数据交换。然而,对于开发者来说,编写和管理API文档以及进行API测试可能是繁琐的任务。Swagger作为一款强大的API文档生成和测试工具,为我们提供了一种简化的解决方案。本文将深入探讨Swagger的基本概念、特点,以及如何在实际应用中使用它进行API文档管理和测试。
78 1
|
4月前
|
前端开发 API
前端学习笔记202305学习笔记第三十一天-什么是mvc-c层api 前后端联动3
前端学习笔记202305学习笔记第三十一天-什么是mvc-c层api 前后端联动3
36 0
|
5月前
|
JSON NoSQL 前端开发
SpringBoot定义优雅全局统一Restful API 响应框架完结撒花篇封装starter组件
SpringBoot定义优雅全局统一Restful API 响应框架完结撒花篇封装starter组件
SpringBoot定义优雅全局统一Restful API 响应框架完结撒花篇封装starter组件
|
7月前
|
消息中间件 Kubernetes Java
「集成架构」我们得谈谈 Apache Camel集成框架
「集成架构」我们得谈谈 Apache Camel集成框架
141 0
|
8月前
|
缓存 Java API
微服务如何聚合 API 文档?这波秀~
微服务如何聚合 API 文档?这波秀~
微服务如何聚合 API 文档?这波秀~
|
机器学习/深度学习 存储 Kubernetes
快速入门统一模型部署(Model Serving)框架 BentoML
BentoML 是一个用于机器学习模型服务的开源框架,旨在弥合数据科学和 DevOps 之间的差距(gap)。 数据科学家可以使用 BentoMl 轻松打包使用任何 ML 框架训练的模型,并重现该模型以用于生产。 BentoML 协助管理 BentoML 格式打包的模型,并允许 DevOps 将它们部署为任何云平台上的在线 API 服务端点或离线批量推理作业。
|
Java 微服务 Spring
微服务项目:尚融宝(11)(后端接口:统一日志处理)
默认情况下,spring boot从控制台打印出来的日志级别只有INFO及以上级别,可以配置日志级别
微服务项目:尚融宝(11)(后端接口:统一日志处理)