一个同时包含增量同步和全量同步的接口设计

你是否还在为设计同步数据接口烦恼呢,同步数据接口需要考虑到增量同步、全量同步、网络等原因,比如网络中断,怎么才能实现断点续传呢?有多个终端需要同步数据如何处理?......

前面我们讲解了“如何设计一个安全的RESTful API协议”,安全我们就不再赘述,今天我们来讲解如何通过一个接口实现全量、增量同步机制,支持多终端同步,同时还解决了断点续传问题,下面直接看接口定义,关注标记红色的部分;

接口全局参数及结构说明:

接口格式:

http://***/apis/{function}

协议类型:

http/https

协议头:

Method:post/get

Content-Encoding:utf-8

Content-Type:application/json

请求参数:

请求参数的内容将放到http form、或者url参数中提交

全局入参:

参数名

appid

int

应用ID(必须,由接口提供方分配,可以用来区分不同的授权方,每个appid对应的secretkey也应不同)

timestamp

string

时间戳(ms)(必须,参与签名,保障每次请求的sig不同,服务端可判断时间的有效性,同时可防止重复请求)

sig

string

签名(必须)Md5("appid#timestamp#secretkey")

其中appid、secretkey为接口提供方分配,secretkey为私钥,不可在协议中明文传输

响应结构:

{

"status" : 0,

"success" : true,

"msg" : "OK",

"data" : object

}

字段

status

int

状态码(0=成功,其他表示失败,当然这里能定义出详细的错误码最好;)

success

boolean

是否成功(status=0,此值为true,否则为false)

msg

string

对应状态码的详细描述

data

object

详细数据,不同接口返回数据不同,可以是JSON,也可以是加密的字符串数据;

根据数据安全要求,可对此数据进行AES加密处理,secretkey可作为加密秘钥,客户端通过secretkey解密即可。

1、获取汽车品牌信息

URL:/apis/getBrands

请求参数格式:

{
  "gtTime": -1,
  "limit":20
}

请求参数字段说明:

参数名

是否必须

gtTime

long

必须

查询数据更新时间大于gtTime的数据,传入-1表示从头开始同步

limitint必须单次返回的记录数

响应数据格式:

{

  "success": true,

  "msg": "OK",

  "status": 0,

  "data": [

    {

      "id": "117",

      "name": "AC Schnitzer",

      "bfirstletter": "A",

      "logo": "/brandimgs/0_117.jpg",

      "country": "德国",

      "info": "1987年创建的AC Schnitzer是全世界最大的BMW专业改装厂,虽然建厂较晚,但因为是世界最大的BMW的经销商Kohl Automobile Gmbh和Schnitzer赛车集团合作创立。在经验和销售两方面都具有全面优势(早在1964年,Schnitzer就已经开始致力于改装BMW并参加各项赛事)。",  
    "isdel": "0",
    "updateTime": "638065594093855886"

    },

    {

      "id": "509",

      "name": "AITO",

      "bfirstletter": "A",

      "logo": "/brandimgs/0_509.jpg",

      "country": "中国",

      "info": "2021年12月,小康股份旗下的赛力斯公司在重庆两江智慧工厂发布和华为合作的高端智慧汽车品牌AITO以及赛力斯纯电驱增程平台(DE-i)。AITO旗下首款搭载最新华为鸿蒙HarmonyOS智能座舱车型问界M5已于2021年12月23日正式发布。AITO意为:Adding Intelligence to AUTO.AITO与AUTO一字之差:“I”,即 Intelligence,代表 HarmonyOS 智能座舱等创新技术能力;通过“I”的赋能,AITO 将智能带入汽车,让汽车更智慧。",
    "isdel": "0",
    "updateTime": "638065594093855887"

    }

  ]

}

响应数据字段说明:

段名

status

int

状态码(0=成功,其他表示失败;)

msg

string

状态码对应的描述

data

[{},{}]

        id

String

ID(品牌ID,唯一键)

        name

String

品牌名称

        bfirstletter

String

首字母

        logo

String

品牌LOGO

        country

String

国家

        info

String

品牌介绍

        isdelint是否删除(0=否,1=是)
        updateTimelong更新时间

       

 下面是服务端示例代码片段如下:

@RequestMapping(value = "/getBrands", method = {RequestMethod.POST, RequestMethod.GET})
    public JSONResponse<List<BrandInfo>> getBrands(HttpServletRequest request,
                                              @RequestParam(value = "gtTime", defaultValue = "-1") Long gtTime,
                                              @RequestParam(value = "limit", defaultValue = "20") Integer limit) {
        try {

            JSONResponse checkResult = CheckSig(request);
            if (!checkResult.isSuccess()) {
                return checkResult;
            }
            if (gtTime == null) {
                gtTime = -1L;
            }
            if (limit == null || limit <= 0 ) {
                limit = 100;
            }
            if (limit > 1000) {
                limit = 1000;
            }
            List<BrandInfo> list = this.brandService.findList(gtTime,  limit);

            return this.success(list);
        } catch (Exception ex) {
            ex.printStackTrace();
            return this.error(ex.getMessage());
        }
    }

Dao层查询数据,只需要查询updateTime字段大于传入的gtTime值即可,示例SQL如下:

select * from t_brandInfo where updateTime > $gtTime order by updateTime  asc limit $limit

 注意,数据表必须加入isdel、updateTime两个字段,不能执行物理删除,只能执行逻辑删除,也就是删除时只能标记为删除状态,同时将更新updateTime的值,这样不论是更新数据还是删除数据,updateTime字段都会更新到最新的时间戳,这样同步数据时只需要检查本地已同步数据的最大值传入接口即可拉取最新更新的数据了,当然需要注意查询语句,必须是order by updateTime  asc哦;

下面是客户端调用同步的代码片段:

public static void main(String[] args) {
        long gtTime = -1;//可以从数据库提取之前数据的最大updateTime值,用于增量同步
        while (true) {
            JSONResponse<List<BrandInfo>> result = getBrands(gtTime, 3);
            if (!result.isSuccess()) {
                System.out.println("同步任务异常终止:" + result.getMsg());
                break;
            }
            if (result.getData().size() == 0) {
                System.out.println("无数据返回,同步任务正常终止:" + result.getMsg());
                break;
            }
            //这里写保存入库的逻辑

            gtTime=result.getData().stream().max(Comparator.comparing(BrandInfo::getUpdateTime)).get().getUpdateTime();//提取本次同步的最大updateTime
        }
    }
    private final static String appid = "1001";
    private final static String secretkey = "********";
    private final static String apihost = "http://***";
    /**
     * 获取品牌
     */
    public static JSONResponse<List<BrandInfo>> getBrands(long gtTime, int limit) {
        try {
            Map<String, Object> parms = new HashMap<String, Object>();
            parms.put("gtTime", gtTime);
            parms.put("limit", limit);

            String content = httpGet("getBrands",  parms) ;
            return JSON.parseObject(content, new TypeReference<JSONResponse<List<BrandInfo>>>() {
            });

        } catch (Exception ex) {
            ex.printStackTrace();
            return JSONResponse.Create(false, ex.getMessage(), null, 501);
        }
    }
    /**
     * 构建带签名的请求URL
     *
     * @param function 请求的接口方法
     * @param parms    接口对应的参数
     * @return
     */
    private static String httpGet(String function, Map<String, Object> parms) {
        long timestamp = System.currentTimeMillis();
        String sig = SecureUtil.md5(appid + "#" + timestamp + "#" + secretkey);
        StringBuilder urlbuilder = new StringBuilder(apihost).append("/apis/").append(function).append("?appid=").append(appid).append("&sig=").append(sig).append("&timestamp=").append(timestamp);
        for (String key : parms.keySet()) {
            urlbuilder = urlbuilder.append("&").append(key).append("=").append(parms.get(key));
        }
        String url = urlbuilder.toString();
        String content = HttpUtil.get(url);
        return content;
    }

同步逻辑:
1、首先查询本地数据库最大的updateTime,如果数据库无记录则计为-1;
2、将查询到的最大updateTime值传入gtTime;
3、当有数据返回时,获取返回记录中最大的updateTime值,传入下一次查询gtTime,直到无记录返回,则同步完成;

 

至此一个同时支持全量、增量数据同步,支持多终端的数据同步,同时还支持断点续传的接口就完成了。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

myshare2022

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值