世上本没有坑,踩的人多了也便成了坑。每遇到一次困难,每踩一个坑,对程序员来讲都是一笔财富。持续学习是程序员保持竞争力的源泉。本期将分享一个踩坑无数的Java程序猿填坑秘籍。前端
榆木,一个阅历无数(踩坑)的技术宅男,喜欢了解新技术却不爱太钻研新技术(由于懒,猿届反面角色一枚)。14年毕业至今,在Java开发这条道路上可谓是坑过好些人、也埋过好些坑、也被坑过好些次。由于懒,没有针对他遇到过的问题作过太多的笔记(记录一些棘手问题的解决方法仍是个不错的习惯),只是习惯性的去分析为何出现这样的问题,咱们该怎么去避免重复出现。在这里榆木share一下第一次作独立需求的过程。mysql
榆木·Java开发工程师程序员
要成为一个合格的Java程序猿,独立完成需求是一个必须经历的阶段,在这个过程当中可能会遇到不少问题,要学会合理的利用资源(官方文档、社区论坛等)去解决问题。在这个阶段应该是踩坑最多、收获最多、成长最快的阶段。sql
在榆木入职的前3个月里,作的都是一些改bug、完善需求的活,他不须要太多思考,根据客户说的作就成了。三个月以后他的公司顺利拿下了该客户的二期项目,因为人手不够,再加上他在一期维护的时候对业务比较熟悉,老大便让榆木独自承担该项目前置子系统的所有需求。刚开始的时候榆木是很激动的,随之而来的倒是不知所措。json
榆木都是如何踩坑又填坑的呢?分享一下他的几点经验,但愿对开发者有所帮助。后端
如何同时开启两个SVN服务服务器
由于公司资源不够,老大就要求在原有的服务器上再弄一个SVN服务,因而他开始各类捣腾,但是无论怎么样就是没有办法同时起来两个服务。怎么办,只能找哥哥(google)帮忙咯,由于SVN服务的启动(/etc/init.d/svnserve start )是包含一些默认参数,如 --listen-port指定服务端口 默认是3691,若是要同时起两个SVN服务只须要在启动时指定两个不一样的listen-port就OK了。ide
以下,问题解决:svn
/etc/init.d/svnserve start -d -r /svn/repo 第一次第一个库启动 默认3691 /etc/init.d/svnserve --listen-port 9999 -d -r /svn/svndata 第二次指定端口启动
问题搞定,紧接着就是紧张的代码开发,事情有点想不到的顺利,后端接口顺利完工经过测试,榆木开始和前端对接联调,好激动,搞很差能够提早完成任务了。噼里啪啦的搞完就开始测试了。
学习
fastJson序列化问题
所谓没有遇到过bug的程序猿就不是正常的程序猿,一点都不意外,问题来了。同一个对象赋值给HashMap中不一样的key 传到前端后,第二个value居然不能被正常解析....... 他本身写的代码必须不能怂,有问题那就解决问题,因而榆木开始找问题所在,开始模拟数据,发现返回结果以下:
{"o1":{"age":16,"name":"o1"},"2":{"$ref":"$.o1"}}
很容易就能看出来,第二value在这个返回结果中用相似指针的方法("$ref":"$.o1")表示它和“o1”的值同样,看起来像是同一个对象的循环引用哦,那是否是能够把这个循环引用禁止呢?答案是能够的。(有必要说明一下,这里使用的是fastJson)经过SerializerFeature指定禁用循环依赖就能够了。修改前代码以下:
public static void test1() { TestObject object = new TestObject("o1", 16); Map<String, TestObject> map = new HashMap<String, TestObject>(); map.put("o1", object); map.put("o2", object); System.out.println(new String(JSON.toJSONBytes(map))); }
输出结果:{"o1":{"age":16,"name":"o1"},"o2":{"$ref":"$.o1"}}
在一个集合对象中存在多条相同数据时,将ist集合对象转化为json对象输出到前台时,JSON默认对第二条数据处理时用"$ref":"$.object".<这里object指第一条数据>,这样的json转化结果输出到前台确定是不可使用的,好在JSON有提供禁止关闭引用循环检测的方法,只须要在转化的时候加上SerializerFeature.DisableCircularReferenceDetect 就能够解决了。修改后代码以下:
public static void test1() { TestObject object = new TestObject("s1", 16); Map<String, TestObject> map = new HashMap<String, TestObject>(); map.put("o1", object); map.put("o2", object); SerializerFeature feature = SerializerFeature.DisableCircularReferenceDetect; System.out.println(new String(JSON.toJSONBytes(map, feature))); }
输出结果以下:{"o1":{"age":16,"name":"o1"},"o2":{"age":16,"name":"o1"}}
到这里问题就解决了。不久以后测试经过了,交付客户测试版本,开始和中心联调测试了。
OOM异常处理
榆木觉得到这里就万事大吉了,然而是不可能的。联调测试两天以后,客户反馈说:“咱们的XXX报文数据已经往中心发过了呀,但是中心说他们没有收到,大家查下是什么问题呗!”客户就是上帝呀,榆木和他的同事开始查询日志,发现有一些OOM的异常。异常产生的场景是在取数据-组报文-MQ转发这个环节,而后就开始一个一个点的排查了。
榆木首先想到的可能缘由有:
一、数据取出来生成报文这个过程都是在内存中作的,会不会是这里数据太多致使?
二、会不会是报文生成过程产生了过多Object没有来得及回收?
三、会不会是数据发送慢于报文生成的速到致使等待队列爆满?
而后开始针对性的作修改测试,他将一次性取数据生成报文的过程改为批量去作,而后测试运行一段时间没有问题(排除 1);在生成保温过程当中,将每个转化后的对象置为空Object=null,以便及时回收,测试运行一段时间没有问题(排除2);在第三点上面,他最早想的是增长线程数量( 服务器开启超线程、应用中增长线程数量)去提高处理速率,运行一段时间以后仍是会出现OOM。怎么办呢?再次回到了等待队列上面来,能不能在必定程度上对等待队列作个限制呢?因而榆木在每次从MQ取消息以前增长了对等待队列的深度的判断,若是深度大于最大线程数量的2倍,就放弃本次MQ队列消息的处理。而后继续测试,问题没有再出现。
查询慢怎么办?
最终项目上线了,终于能够松一口气了。但是有一天,榆木的老大说客户反映部分查询很慢,让他去处理一下。榆木内心想着,这个应该是个小问题,给数据表加索引就能搞定。到了客户现场以后发现,原来的表是有索引的,可查询仍是慢,不得已只能去找缘由了。不得不说,explain SQL是个不错的命令,发现索引没有生效,通过仔细的比对,发现该关联查询的关联字段在两个表中都有索引, 两个表的字符集都是UTF8,可是排序规则一个是utf-bin(二进制存储数据,大小写区分),一个是utf8_general_ci(大小写不敏感),因此把数据排序规则改为一致索引生效,查询速度也就上来了。
PS: mysql中的UTF-8编码的排序规则说明
utf8_bin将字符串中的每个字符用二进制数据存储,区分大小写。
utf8_genera_ci不区分大小写,ci为case insensitive的缩写,即大小写不敏感。
utf8_general_cs区分大小写,cs为case sensitive的缩写,即大小写敏感。
【写在最后】
榆木整理了下这些年踩的坑,给本身也给正在和他挣扎在挨踢坑里的小伙伴们一些启发与鼓励。持续学习是保持竞争力的前提;夯实的基础是进阶的垫脚石。抬头走不独行(exchange)、埋头干(code),就算被称做屌丝,也仍是要有梦想,万一逆袭了呢。
若是你也愿意分享你的故事,请加51CTO开发者QQ交流群 114915964联系群主小官,期待你精彩的故事!