团队的项目正常运行了很久,但近期偶尔会出现BUG。目前观察到的有两种场景:一是大批量提交业务请求,二是生成批量导出文件。出错后,再执行一次就又正常了。

经过跟踪日志,发现是在Server之间进行json格式大数据量传输时会丢失部分字符,造成接收方拿到完整字符串后不能正确解析成json,因此报错。

同其他团队同事们沟通后发现,不仅仅是我们项目有这个问题,我们不是一个人在战斗。

1 问题现象

服务器之间使用http+json的数据传输方案,在传输过程中,一些json数据发生错误,导致数据接收方解析json报错,系统功能因此失败。

下面截取了一小段真实数据错误,在传输的json中,有一个数据项是departmentIdList,其内容时一个长整型数组。

传输之前的数据为:

"departmentIdList" : [ 719, 721, 722, 723, 7367, 7369, 7371, 7373, 7375, 7377 ]

接收到的数据为:

"departmentIdlist" : [ 719, 721'373, 7375, 7377 ]

可以看到,这个错误导致了两个问题:

1、 json解析失败

2、 丢失了一些有效数据

详细检查系统日志之后,这是偶发bug,并且只在传输数据较大时发生。

2 可选的解决方案

2.1 请架构组协助解决

这是最直接的解决方案,因为我们项目使用架构组提供的环境,他们需要提供可靠的底层数据传输机制。

2.2 压缩传输数据

因为数据量大时容易发生,并且传输的都是普通文本,可以考虑对内容进行压缩后传输。普通文件压缩率也很高,压缩后内容长度能做到原数据10%以内,极大减少传输出错的几率。

2.3 对传输数据进行MD5校验

将传输数据作为一个完整数据块,传输之前先做一个md5摘要,并将原数据和摘要一并发送;接收方收到数据后,先进行数据校验工作,校验成功后再进行后续操作流程,如果不成功可以辅助重传或直接报错等机制。

3 方案设计

为了彻底解决这个问题,设计了一个底层方案

3.1 设计原则

1、 适用类型:Spring MVC项目,数据发送方使用RestTemplate工具类,使用fastjson作为json工具类。

2、 数据校验,使用MD5加密,当然也可以配合数据压缩机制,减少传输数据量。

3、 提供底层解决方案,不需要对系统代码做大规模调整。

3.2 核心设计

数据发送方,重载RestTemplate,在数据传输之前对数据进行md5摘要,并将原始数据和 md5摘要一并传输。

数据接收方,重载AbstractHttpMessageConverter,接收到数据后,对数据进行MD5校验。

3.3 DigestRestTemplate关键代码

对原json进行摘要,并同原始数据一起生成一个新的json对象。

private Object digestingJson(JSONObject json) throws Exception {

String requestJsonMd5 = JsonDigestUtil.createMD5(json);

JSONObject newJson = new JSONObject();

newJson.put("content", json);

newJson.put("md5", requestJsonMd5);

return newJson;

}

重载的postForEntity函数核心部分,如果传入参数是 JSONObject,则调用方法对数据进行摘要操作,并用新生成的json进行传输。

Object newRequest = null;

if (request instanceof JSONObject) {

JSONObject json = (JSONObject) request;

try {

newRequest = digestingJson(json);

} catch (Exception e) {

}

}

if (newRequest == null) {

newRequest = request;

}

return super.postForEntity(url, newRequest, responseType);

3.4 DigestFastJsonHttpMessageConverter 核心代码

首先会判断是否是经过md5摘要的json,是有摘要的数据进行校验,否则直接返回对象。

private JSONObject getDigestedJson(JSONObject json) {

if (json.size()==2&&json.containsKey("md5")&&json.containsKey("content")) {

String md5 = json.getString("md5");

String content = json.getString("content");

logger.info("degested json : {}", json);

try {

String newMd5 = JsonDigestUtil.createMD5(content);

if (newMd5.equals(md5)) {

json = JSON.parseObject(content);

} else {

logger.error("md5 is not same : {} vs {}", md5, newMd5);

throw new RuntimeException("content is modified");

}

} catch (Exception e) {

}

} else {

logger.info("may not be digested json");

}

return json;

}

原有的处理数据代码增加调用该方法的代码

@Override

protected Object readInternal(Class<? extends Object> clazz,

HttpInputMessage inputMessage)

throws IOException, HttpMessageNotReadableException {

JSONObject json = null;

InputStream in = inputMessage.getBody();

Charset jsonCharset = fastJsonConfig.getCharset();

Feature[] jsonFeatures = fastJsonConfig.getFeatures();

json = JSON.parseObject(in, jsonCharset, clazz, jsonFeatures);

json = getDigestedJson(json);

return json;

}

当前的代码,如果数据校验失败,简单抛出异常。后续可以增加更多的机制,比如在RestTemplate处增加校验,如果发现校验失败,则重传。

3.5 数据发送方项目配置

以Spring Boot项目为例

在Main类中定义 restTemplate

@Bean(name = "restTemplate")

public RestTemplate getRestTemplate() {

RestTemplate restTemplate = new DigestRestTemplate();

return restTemplate;

}

需要调用RestTemplate的代码,只需要依赖注入RestTemplate

@Autowired

RestTemplate restTemplate;

3.6 数据接收方项目设置

在SpringBootApplication类中定义

@Bean

public HttpMessageConverters fastJsonHttpMessageConverters() {

DigestFastJsonHttpMessageConverter fastConverter =

new DigestFastJsonHttpMessageConverter();

FastJsonConfig fastJsonConfig = new FastJsonConfig();

fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);

fastConverter.setFastJsonConfig(fastJsonConfig);

HttpMessageConverter<?> converter = fastConverter;

return new HttpMessageConverters(converter);

}

4 后记

经过测试,这个方案是可行的。如果为了能够适应更多的项目及更多的Java技术栈,需要对代码进行进一步完善。

SpringMVC底层数据传输校验的方案的更多相关文章

  1. SpringMVC底层数据传输校验的方案(修改版)

    团队的项目正常运行了很久,但近期偶尔会出现BUG.目前观察到的有两种场景:一是大批量提交业务请求,二是生成批量导出文件.出错后,再执行一次就又正常了. 经过跟踪日志,发现是在Server之间进行jso ...

  2. springmvc的数据校验

       springmvc的数据校验 在Web应用程序中,为了防止客户端传来的数据引发程序异常,常常需要对数据进行验证,输入验证分为客户端验证与服务器端验证. 客户端验证主要通过javaScript脚本 ...

  3. springmvc(四) springmvc的数据校验的实现

    so easy~ --WH 一.什么是数据校验? 这个比较好理解,就是用来验证客户输入的数据是否合法,比如客户登录时,用户名不能为空,或者不能超出指定长度等要求,这就叫做数据校验. 数据校验分为客户端 ...

  4. SpringMVC学习(三)———— springmvc的数据校验的实现

    一.什么是数据校验? 这个比较好理解,就是用来验证客户输入的数据是否合法,比如客户登录时,用户名不能为空,或者不能超出指定长度等要求,这就叫做数据校验. 数据校验分为客户端校验和服务端校验 客户端校验 ...

  5. 深入源码分析SpringMVC底层原理(二)

    原文链接:深入源码分析SpringMVC底层原理(二) 文章目录 深入分析SpringMVC请求处理过程 1. DispatcherServlet处理请求 1.1 寻找Handler 1.2 没有找到 ...

  6. 设计模式:与SpringMVC底层息息相关的适配器模式

    目录 前言 适配器模式 1.定义 2.UML类图 3.实战例子 4.总结 SpringMVC底层的适配器模式 参考 前言 适配器模式是最为普遍的设计模式之一,它不仅广泛应用于代码开发,在日常生活里也很 ...

  7. Android基于WIFI实现电脑和手机间数据传输的技术方案研究

    Android手机和电脑间基于wifi进行数据传输,从技术上讲,主要有两种方案: 一种是通过ftp协议实现,Android手机作为数据传输过程中的ftp服务器: 一种是通过http协议实现.Andro ...

  8. SpringMVC + MyBatis分库分表方案

    mybatis作为流行的ORM框架,项目实际使用过程中可能会遇到分库分表的场景.mybatis在分表,甚至是同主机下的分库都可以说是完美支持的,只需要将表名或者库名作为动态参数组装sql就能够完成.但 ...

  9. 学习SpringMVC必知必会(7)~springmvc的数据校验、表单标签、文件上传和下载

    输入校验是 Web 开发任务之一,在 SpringMVC 中有两种方式可以实现,分别是使用 Spring 自带的验证 框架和使用 JSR 303 实现, 也称之为 spring-validator 和 ...

随机推荐

  1. 解决WebSocket兼容ie浏览器版本问题

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7942323.html 在使用Netty进行WebSocket开发时,测试发现:ie 11系列个别低版本连接W ...

  2. 单节点下使用docker部署consul

    部署consul 目前Consul使用的版本是: v1.0.1 本教程适用于刚刚开始学习consul并简单使用consul的同学,可以在短时间内了解conusl,配合官方文档https://www.c ...

  3. 这一次带你彻底了解Cookie

    前言 网络早期最大的问题之一是如何管理状态.简而言之,服务器无法知道两个请求是否来自同一个浏览器.当时最简单的方法是在请求时,在页面中插入一些参数,并在下一个请求中传回参数.这需要使用包含参数的隐藏的 ...

  4. ACM HDU 1081 To The Max

     To The Max Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) To ...

  5. CI框架使用PHPmailer发送邮件找回密码

    之前用PHP+Mysql+jQuery结合ThinkPHP做了一个用户验证邮箱找回密码功能<ThinkPHP之PHP+Mysql+jQuery发送邮箱找回密码>,现在分享一下用CI框架结合 ...

  6. QT之UDP通信

    前言:前一篇讲了TCP通信,这篇来看看UDP通信. 这里说明一下,UDP通信中分为三种通信分别为单播.组播和广播,下面将一一为大家介绍. 同样的我们都需要在工程文件中添加network QT += c ...

  7. UWP 实现App多语言为所欲为切换

    为所欲为,嗯 话不多说,先看效果吧(事先说明,我的方法不是最好的,但是我用着最有效.) [吐槽一下博客园上传的图片,我的App敲鸡漂亮滴,自带亚克力效果,怎么图片上传上来这么多的噪点啊.] [ 商店地 ...

  8. Maven项目pom.xml文件报xxx\target\classes\META-INF\MANIFEST.MF (系统找不到指定的路径)

    在今天的学习Maven项目中遇到的这个错误:pom.xml文件报xxx\target\classes\META-INF\MANIFEST.MF (系统找不到指定的路径) 在Maven项目学习中,缓存问 ...

  9. webpack 图片的打包

    1. 在img文件夹下随便找一个小一点的图片放进去. 2.修改entry.js require('../css/index.css'); var demo1 = require('../js/demo ...

  10. Markdown语法入门

    本文内容参考与这里,本篇文档,用Markdown语法写成. 概述 宗旨 Markdown 的目标是实现「易读易写」. 可读性,无论如何,都是最重要的.一份使用 Markdown 格式撰写的文件应该可以 ...