工作中遇到的坑-长期记录

1、spring上传文件过大导致请求重复多次,无返回值页面假死

项目里面需求遇到需要上传图片的功能,用了spring的CommonsMultipartResolver来处理。设置了maxUploadSize,
本地测试的时候,上传过大的文件,总是页面假死,异常也无法捕获,折腾半天,才知道这是tomcat版本的一个bug,
apache-tomcat-7.0.70 会有这个bug,换成 apache-tomcat-7.0.80 就好了。

参考网址:https://bz.apache.org/bugzilla/show_bug.cgi?id=57438

2、aes加密javax.crypto.BadPaddingException: Given final block not properly padded

1
2
3
4
5
本地可能加解密正常,到linux服务器上面就解密异常,解决方法如下
解密时候指定算法名称生成随机数
SecureRandom random=SecureRandom.getInstance("SHA1PRNG");
random.setSeed(key.getBytes());
kgen.init(keysize, random);

3、httpclient超时时间设置过大,导致线上tomcat内存溢出

公司wap网站登陆的时候,会调钱包组的http接口,刚好早上他们的接口挂了,而我们的老代码里面设置的超时时间是30s,
碰巧早上公司还有好几波秒杀和团购活动,一下子并发流量过来,导致系统有大量的httpclient对象没被回收,
导致tomcat内存溢出,直接挂了!
对于httpclient,最好可以以连接池方式提供调用,这样可以控制httpclient的实例数量,
超时的时间最好可以动态控制
比如定时去访问某个服务接口,如果某个服务接口不可以用,超时时间就设置成0,
直到服务可以用了,定时才将超时的时间改成指定的时间

4、maven打包卡死在downloading

找到仓库<user_home>/.m2/repository/中相应目录下的.lock文件,删除就可以了。

5、json转换成object的工具类慎用或者object转json工具类(下面的例子)

如fastJson的
public static final T parseObject(String text, Class clazz); // 把JSON文本parse为JavaBean
如果我们引用其他组的接口,然后对json返回结果直接用上面的方法,会有隐藏的坑。
1、如果其他组修改了返回结果的json串,比如变动了某些key,或者修改了某个key的属性,会直接导致我们线上引用的项目挂了!

6、不要在getter/setter里面加任何业务逻辑

前几天才看阿里巴巴技术手册里面就有这一条强制的规则,今天就遇到这种大坑。
线上遇到一个死循环,循环调用搜索里面的某个方法,直接导致tomcat挂了,比较坑的是在某些情况下才会
触发这个死循环。后来定位问题是,某个属性的get方法里面加了业务逻辑,这个逻辑会导致循环调用,一般情况下
不调用get方法是不会触发的,但是如果我们调用toJSONString(Object object); // 将JavaBean序列化为JSON文本
就会把所有属性get方法都会调用一遍的。

7、java 8小时时差的问题

    昨天在线上遇到一个问题,明明该推送的定时数据没有执行到,检查了sql和看了下代码的提交记录最近几天都没有变动,
然后登录跳板机到数据库里面执行sql,发现数据是存在的。后来在业务平台查询数据的时候 发现所有带有日期字段的数据都少了
8小时。比如数据库里面显示的是 2017-04-06 10:52:20,业务平台显示的就是2017-04-06 02:52:20.
因为java在启动的时候会获取当前服务器的时区。
解决方法:1、java设置时区:System.setProperty("user.timezone","****")
          2、Linux系统修改时区:tzselect 一步一步选择   date -R 可以查询当前所在时区

7、springmvc @CookieValue获取相同cookie key的问题

 cookie没有删除功能,如果要删除某一个cookie的值,一般都是重新生成相同key的cookie,然后设置失效时间为0。但是有时候也会
在不同的域名下面有相同的key,比如域名m.ule.com和.ule.com,如果项目是在m.ule.com域名下,那么@CookieValue这个注解获取的就是当前
m.ule.com的cookie信息

8、Linux OOM Killer导致java进程退出

beta系统部署的项目最近几次总是无缘无故的挂了,没有任何错误日志,后来分析应该是java进程被 linux oom 杀掉了。
OOM killer(Out-Of-Memory killer),该机制会监控机器的内存资源消耗。
当机器内存耗尽前,该机制会扫描所有的进程(按照一定规则计算,内存占用,时间等),挑选出得分最高的进程,
然后杀死,从而保护机器。
可以通过 dmesg | grep java 或者 /var/log/messages 定位关键字:oom-kille 定位oom

8、域名切换问题

因为其他组要域名切换,所以直接把对方的地址换成新的域名,没想到返回结果少了参数,以前是带callback()的
新的不带callback(),换句话说其他组是两套代码并行的,大坑啊,以后上线一定要注意这种情况。

9、包装类类型转换导致的空指针问题。

比如 int a= bean.getAge();
getAge()返回的是Integer类型,如果底层返回的是null,再将null赋值给int 就会导致空指针异常

10、线程池遇到System.exit(0),导致线程提前退出的情况。

写定时任务job的时候,会在程序最后一段加上System.exit(0);
pool.shutdown();
System.exit(0);
当程序执行到System.exit(0);时候,会直接kill掉程序,导致一些thread没有被执行到,改成以下写法
 pool.shutdown();
 pool.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
 System.exit(0);
 会等待所有线程都执行完毕才退出程序。

11、接口DTO里面最好不要定义枚举类型

在远程方法调用过程中,如果我们发布的客户端接口返回值中使用了枚举类型,
那么服务端在升级过程中就需要特别注意。如果在接口的返回结果的枚举类型中添加了新的枚举值,
那就会导致仍然在使用老的客户端的那些应用出现调用失败的情况。因此,针对以上两种情况,
应该尽量避免使用枚举,如果实在要用,也需要仔细设计,因为一旦用了枚举,有可能会给后期维护带来隐患。

12、数据库用户ID用int类型的坑

最近公司在做用户id 从int类型转换成long类型的迁移,因为int类型最大为2的31次方-1,也就是2147483647 大概就是21亿
随着用户量的递增,在可预见的时间内肯定会超过int的最大类型,所以以后定义在用户id的时候,最好要有远见,直接定义成long类型。

13、日志的坑

日志学习

高并发的时候会影响性能的操作
1、不要使用log4j,因为log4j打印日志的时候是串行,会阻塞主线程。
2、线上不要你使用Console打印日志,Console日志也是同步的操作。
3、调用频繁的接口不要不要乱打日志,会把磁盘堆积慢,例如消费kafka的消息

14、接口超时

当我们给第三提供serice接口时候,需要注意接口的性能问题,不然很容易超时。
1、限制请求参数的数量
比如根据shopId查询门店信息,如果下游传N个shopId,那么一次性批量查询的时候会拖垮上游服务。
2、有循环的地方都需要注意
例如根据customerId查询下面关联的shopId,一般普通客户就挂一两个,如果是大客户,可能挂几百个门店
如果串行调用肯定会超时,这时候就需要提前并发处理

15、mysql版本导致的字符集问题

SELECT VERSION() 查询mysql版本
mysql 5.6以上都支持表情字符集,但是如果是5.6版本的,
需要在数据库连接上配置connectionInitSql: set names utf8mb4,不然就算数据库支持,代码访问的时候
插入表情符号还是会报错,5.7以上的数据库连接不配置,默认是支持字符集的。

16、mysql limit分页返回重复的数据

select * from table order by xx limit 0,10
当xx不存在索引,且有xx相同的行是,可能出现分页数据重复问题
在MySQL 5.6的版本上,优化器在遇到order by limit语句的时候,做了一个优化,即使用了priority queue。使用 priority queue 的目的,就是在不能使用索引有序性的时候,如果要排序,并且使用了limit n,那么只需要在排序的过程中,保留n条记录即可,这样虽然不能解决所有记录都需要排序的开销,但是只需要 sort buffer 少量的内存就可以完成排序。之所以5.6出现了第二页数据重复的问题,是因为 priority queue 使用了堆排序的排序方法,而堆排序是一个不稳定的排序方法,也就是相同的值可能排序出来的结果和读出来的数据顺序不一致。5.5 没有这个优化,所以也就不会出现这个问题

17、SpringBoot,返回JSON,Long前端精准度丢失

因为项目需要分布式ID,所以雪花算法生成的long ID 在返回时候会丢失精度,需要自己定义json注解
@JsonComponent
public class JsonSerializerManage {

    @Bean
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
        //忽略value为null 时 key的输出
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        /**
         * 序列换成json时,将所有的long变成string
         * 因为js中得数字类型不能包含所有的java long值
         */
        SimpleModule module = new SimpleModule();
        module.addSerializer(Long.class, ToStringSerializer.instance);
        module.addSerializer(Long.TYPE, ToStringSerializer.instance);
        objectMapper.registerModule(module);
        return objectMapper;
    }

}

18、mysql row模式主从延迟

MySQL 在使用ROW 行模式的时候,最大化保证主从复制的一致性,在主库操作的任何一条sql,都会以涉及变动的每行来记录
这样导致的坏处就是生成的binlog日志非常大
例如:update t set a=b where id>=10 ,假设涉及改动20条记录,那么在从库里面就会执行20次,这时候如果从库里面的表没有
索引,那么延迟就老大了。因为在主库就执行一次的,到从库就要执行20次。。。