XX系统中,一个用户须要维护的项目数过多,填写的任务数超多,产生了一次工时保存中,只有前面一部分的xx数据持久化到数据库,后面的数据没有保存。前端
图1
java
首先想到的是否后面部分的数据在保存过程当中发生了异常。排查异常日志,发现没有该问题存在。nginx
而后增长方法参数信息日志,数据参数信息。发现参数集合size=200,前端发送集合size=400。判断问题能够能是由于服务器容器环境(Nginx+Tomcat)致使web
在测试环境模拟线上数据。如图1
spring
在idea中直接启动tomcat,无nginx环境,若是没有问题,则可暂时肯定为nginx问题。数据库
然而,在过程当中发现了新的问题。tomcat
org.springframework.beans.InvalidPropertyException: Invalid property 'detail[256]' of bean class [com.suning.asvp.mer.entity.InviteCooperationInfo]: Index of out of bounds in property path 'detail[256]'; nested exception is java.lang.IndexOutOfBoundsException: Index: 256, Size: 256 at org.springframework.beans.BeanWrapperImpl.getPropertyValue(BeanWrapperImpl.java:833) ~[spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE] at org.springframework.beans.BeanWrapperImpl.getNestedBeanWrapper(BeanWrapperImpl.java:576) ~[spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE] at org.springframework.beans.BeanWrapperImpl.getBeanWrapperForPropertyPath(BeanWrapperImpl.java:553) ~[spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE] at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:914) ~[spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE] at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:76) ~[spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE] at org.springframework.validation.DataBinder.applyPropertyValues(DataBinder.java:692) ~[spring-context-3.1.2.RELEASE.jar:3.1.2.RELEASE] at org.springframework.validation.DataBinder.doBind(DataBinder.java:588) ~[spring-context-3.1.2.RELEASE.jar:3.1.2.RELEASE] at org.springframework.web.bind.WebDataBinder.doBind(WebDataBinder.java:191) ~[spring-web-3.1.2.RELEASE.jar:3.1.2.RELEASE] at org.springframework.web.bind.ServletRequestDataBinder.bind(ServletRequestDataBinder.java:112) ~[spring-web-3.1.2.RELEASE.jar:3.1.2.RELEASE]
查看BeanWrapperImpl源码服务器
else if (value instanceof List) { int index = Integer.parseInt(key); List list = (List) value; growCollectionIfNecessary(list, index, indexedPropertyName, pd, i + 1); value = list.get(index);// 测试报错时,此处list只有256个,index为256时,取第257个报错 }
@SuppressWarnings("unchecked") private void growCollectionIfNecessary( Collection collection, int index, String name, PropertyDescriptor pd, int nestingLevel) { if (!this.autoGrowNestedPaths) { return; } int size = collection.size(); // 当个数小于autoGrowCollectionLimit这个值时才会向list中添加新元素 if (index >= size && index < this.autoGrowCollectionLimit) { Class elementType = GenericCollectionTypeResolver.getCollectionReturnType(pd.getReadMethod(), nestingLevel); if (elementType != null) { for (int i = collection.size(); i < index + 1; i++) { collection.add(newValue(elementType, name)); } } } }
根据上面的分析找到autoGrowCollectionLimit的定义app
public class DataBinder implements PropertyEditorRegistry, TypeConverter { /** Default object name used for binding: "target" */ public static final String DEFAULT_OBJECT_NAME = "target"; /** Default limit for array and collection growing: 256 */ public static final int DEFAULT_AUTO_GROW_COLLECTION_LIMIT = 256; private int autoGrowCollectionLimit = DEFAULT_AUTO_GROW_COLLECTION_LIMIT;
解决方案,是在本身的Controller中加入以下方法ide
@InitBinder protected void initBinder(WebDataBinder binder) { binder.setAutoGrowNestedPaths(true); binder.setAutoGrowCollectionLimit(1024); }
==BUT 这个问题和线上的不一样,只能算是意外收获。革命还没有成功,同志仍需努力!!!!==
通过2.2的奋斗,暂时断定是否为Nginx post请求参数作了限制。嗯,开搞~ 在开发环境配置Nginx代理,过程略·····
nginx.conf 以下
upstream xxxxxxx { server 127.0.0.1:8080 weight=10 max_fails=2 fail_timeout=30s ; } server { listen 80; server_name xxxxxxx.com; client_max_body_size 100M; # 配置post size #charset koi8-r; #access_log logs/host.access.log main; location / { #proxy_next_upstream http_500 http_502 http_503 http_504 error timeout invalid_header; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://xxxxxxx; expires 0; } }
对于client_max_body_size 100M;
,网上都是与文件上传相关的。不过都是经过post, request body的方式上传数据,因此通用。
测试~~
功能正常,没有重现线上问题。 哭死~~~
革命还要继续~~
去线上服务器拉去配置
<Connector port="1601" maxParameterCount="1000" protocol="HTTP/1.1" redirectPort="8443" maxSpareThreads="750" maxThreads="1000" minSpareTHreads="50" acceptCount="1000" connectionTimeout="20000" URIEncoding="utf-8"/>
经分析,发现线上没有body size的配置,却有maxParameterCount="1000"
。该参数为限制请求的参数个数,从而变相限制body size。
在开发环境配置该参数,测试,问题重现。
问题缘由定位好了,剩下的就是如何解决了。
两个方案:
该上实施难度系数高,由于公司使用的统一发布部署平台,开发人员无服务器操做权限。
修改保存逻辑,分片存储
问题排查,须要先对总体有个把握,而后分析影响范围。不能钻牛角尖,采用西医“头疼医头”的方式。有可能最后结果仍是要医头,但此时的医头已是创建在中医的辩证主义上,对症下药。