框架应用中如何更好地解决问题

现代应用开发中或多或少会使用到各种开源,自研的库,框架等,各种资料良莠不齐, 除了部分成熟开源,商业框架的文档做得很好,使用广泛,生态完整,的确很多问题可以处理。 但自研的通常就是重灾区,资料少,不同步, 那么如何在这类这类框架中找到问题处理的方向,甚至解决方案呢?

昨天帮同事处理了一个uee框架使用的问题,可做借鉴。

1.对框架有个基本了解,例如框架的大体结构,层次,work流程。 对于各个部分如何work这块我认为是很关键的, 例如以安全界大哥大struts2为例,理解一个action的生成,要配置哪些文件, 这些文件怎么配合,生效路径是怎样的,和其他框架如spring怎么结合, 整个过程可以能在脑子里放电影式过一遍,当然能高清就最好了。 这个主要是建立框架的使用模型,方便和其他框架进行类比,解决一些基本的使用问题, 并为后续深入打好基础。

2.了解框架的实现原理,假设你有打开uee对应的生成件脚本文件, 就会发现里边有5w行js,连ide都会卡顿一下,怎么破?其实还是有门路的,因为uee是基于ng1的框架, 这个文件里边有一大半是jq和ng1的代码,后面部分才是扩展功能。 所以掌握ng1的框架原理至关重要,例如dirty check,directive这些黑科技, 以昨天的问题为例,就是直接进到对应directive的实现中,对比行为差异, 最后可以发现多配置一个参数就可以解决问题。 理解关键特性的实现原理,这样在调试验证过程中才心里有底,不会瞎蒙。

3.工欲善其事,必先利其器,熟练掌握各种工具,小到调试技巧,大到各种辅助工具。 调试技巧,例如上面的问题调试,一次触发会数十次call,一个个查看很容易miss target,这个时候就应该用条件断点。 类似的技巧还有异常断点(查找某个特定异常),赋值(改变运行路径)等。 如果大段代码不知哪里有bug,可以两分搜索,在断点处验证正确性。 调试不是用来找bug的,它只是验证的过程中顺手找到bug。 至于辅助工具,反编译工具,抓包工具,各种shell命令,脚本语言,技多不压身,总有合适的。

4.注意积累,适时总结。很多人不是厉害,只是踩坑多而已。 所以还是要多填坑,多填别人的坑,这样就显得很厉害了,填坑机会就更多了,这是个良性循环。 当然万事开头难,一开始还是要注重编程规范学习,看点书, 遇到问题的时候多找找资料,争取系统的过一下相关知识点。 例如你今天遇到了classdefnotfoundexception,问题解决了, 那么就看看这种异常有哪些情况下会出现,怎么避免,和其他相关异常有什么区别? 可能有很多人已经知道了,写出来很low,但做一下总结总是有好处的,日积月累,做好填坑的准备是关键。

书写节奏有点乱,非喜勿喷。今日@广州地铁。

关于攻城狮技术能力提升的一点看法

今天听到有同事说起,网上有技能培训的网课,视频教学,讲解一些’高级’技术,例如从头构建一个tomcat,实时演示。网课价格数千块,对于搞毕业没多久的同学,是一笔不小的开支。

对于这个事情,我是这么看的:

  1. 网课不一定和你的学习能力同步,教学嘛,总是要照顾大多数人,有些人会觉得太快,有些人却嫌慢。所以最好还是可以有针对性,系统性的自学。
  2. 高大上的技术不一定像表面看的那么好。说自己有搞hadoop,却MapReduce都不知道的我也见过。基础说白了,打好扎实的基础才是关键,高大上的技术按需分配。
  3. 实用软件通常都很复杂,有很多corner case需要处理,架构很大可能是比较清晰的,但是代码绝对不是复杂度小于5的。细节是魔鬼,还好大多数情况只要了解关键设计即可,这块通过一些设计文档,讲座就可以了解到。
  4. 知行合一才是理想的做法,我认为有些同学可能实践太多了,知识积累不够,’自己做的东西不够高大上’这种感觉非常突出,迫切需要掌握高大上的技术。或许我们做的东西是有点low,但也不是毫无价值可言,重要还是平时有没有注意积累。

说到知行合一,的确是攻城狮技术能力进阶的关键,一方面可以在项目中挖掘,一方面可以业余时间造轮子,即使非常简陋。

在项目中挖掘,以一个普通的web工程为例,用了servlet,那么这个规范说了些什么,有没有规范文档可以看看?用到了spring,那么它是怎么生成对象的,会用到哪个反射相关的api?一个web因为是基于http的,那么这个协议细节是怎样的?这些都是比较大的,还有很多小的,例如有些地方用了线程本地变量,这个东西有什么缺点?为什么有些地方用encodrURI,有些又是getBytes,还用不同的字符编码?有人用了ConcurrentHashMap,它是怎么实现的?还有哪些线程安全的工具类?看到很多代码用StringBuffer,为什么不是用StringBuilder?在finally里边直接关闭两个流,为什么findbugs会警告?

如果觉得上面的内容脱离实际,那么就造个轮子吧,挑个简单点的,就静态服务器吧,概念自己脑补,那需要怎么做呢?

既然是个服务器,那么得给人访问吧,需要支持个http协议,怎么破?噢,可以用socket编程,然后实现http协议,静态服务器嘛,先支持最简单的Get吧。 现在有个服务了,从Get请求路径中获取要访问的文件路径,然后拼接上指定的根目录,就知道文件地址了,在用文件读取后返回就可以了。

大功告成。如果上2步都可以顺利完成,的确是接近一个玩具的样子了。接下来补充一些功能。

  • 如果你是用阻塞io的话,那么总会涉及多线程,那么是否需要考虑线程池?
  • http请求的连接什么时候关闭?一请求一关闭虽然符合规范,但是keepalive是不是更好点?
  • 请求有可能超时,应该怎么处理?
  • 根目录总是不能写死吧,端口不能写死吧,应该搞成配置,是否考虑一下hot reload?
  • 每次请求都读取文件,看上去比较low,是不是要加个缓存,又要考虑内存占用,是不是考虑LRU?
  • 考虑一下安全吧,避免目录遍历?
  • http得考虑一下客户端缓存吧,例如expired,last_modified,tag等特性吧

看上去是有个玩具的样子了,如果都能搞定,说明对静态服务器的特性,http协议,浏览器行为,jdk的使用,设计模式的运用有更加深入的理解了。成熟的软件只是做的更加精细,考虑了更多的corner case而已。没有足够的基础,只能是在数十万代码中迷失方向什么也无所得。

祝好运。

java攻城狮基础-总纲

java是一门语法相对简单的流行语言,学习曲线比较低,很多童鞋做了太久的CRUD,误以为自己已经对java相当熟悉。其实,我们说的java,不单单是java语法,还包括java惯用法,常见jdk库的细节,jvm相关的基础知识,常见周边框架原理等,是一个大的体系,并不容易掌握。

我计划写一系列的基础文章,一方面看看自己掌握程度如何,查漏补缺,另一方面是看看自己能不能把重点说清楚,当作一次重新学习的机会。

我先简单整个列表,包括一些常见的基础性话题,后续再进行根据情况调整。写作形式以小文章为主,争取半小时以内可以理解的内容,并附带一些思考题。

  • 面向对象特征,包括封装,继承,多态
  • 原生类型与包装类型,原生类型是其他结构的基础,识别两者在内存上的区别,开封箱特性,了解包装类型的实现,如特定整型对象的缓存。
  • 匿名类、内部类、静态内部类,了解他们之间的区别,还有编译后的实际结构(如何转换为普通类)。
  • 可变长参数与数组参数,了解可变长参数在编译层面的实现,了解存在重载情况下可能的混淆问题。
  • 内部变量初始化、构造方法、静态变量初始化、静态块、及存在继承关系时的执行顺序,原则:父类优先,静态常量优于静态块
  • equals,hashcode用途,实现原则,hashcoe要实现简单,和equals保持一致,equals要确保自反性,对称性,传递性,一致性。了解hashcode和equals的应用场景。
  • 对象的强、软、弱和虚引用,了解各种引用的区别及应用场景 http://www.importnew.com/20468.html
  • 常见集合类的特性,应该能够在实际作用中灵活使用,掌握区别及实现原理,列表(ArrayList Vector LinkedList Queue Stack Deque PriorityQueue),散列(HashMap LinkedHashMap TreeMap IdentityHashMap EnumMap WeakHashMap HashTable SortedMap),集合(HashSet TreeSet EnumSet) 需要整理一个特性比较的表格 待整理
  • 多线程基础 java线程模型,线程生命周期,sleep,wait,notify,notifyall,join等线程通信机制。理解同步,原子性,可见性,CAS无锁机制等概念,了解常见锁类型。
  • 并发工具类,掌握常见并发工具类的特性,使用场景,使用方法。了解相关的实现原理。主要包括Executor,Atomic类,ConcurrentHashMap,CopyOnWriteArrayList,BlockingQueue,Latch,Barrier,Condition,Lock等等
  • 擦除型泛型,上限,下限,掌握泛型的作用,如何保持旧代码兼容
  • String、StringBuffer和StringBuilder类的应用场景,区别
  • 正则表达式,掌握常见正则表达式写法,了解正则表达式实现
  • Checked异常和Runtime异常体系,异常的性能,实践中异常的设计与统一处理,包括安全性设计
  • 字符,字节,io流,掌握java流设计体系,字符编码相关知识点,编码转换特性
  • 序列化,序列化应用场景,性能,安全性方面
  • java8最新特性 http://m.open-open.com/m/lib/view/1403232177575.html#methodReferences

认真看待java web基础

很多人一开始接触java技术,做的项目都是web相关的,搞过servlet,jsp,struts,springmvc,用过tomcat,终于感觉是web开发没什么问题了,简历可以标上熟悉java web开发。

现在我们从基本的war包开始,重新梳理一下java web基础,权当复习。

曾经我面试问一个小问题,你知道war包是怎样的结构么?有人会说有src,还有webroot,webapp之类的目录,有人说有平级的classes和WEB-INF目录.

最常见的格式是这样的

xx.war
  WEB-INF
    classes
      com/xxx/A.class
      conf
         xx.properties
      log4j.xml
    lib
      *.jar
    web.xml
  *.jsp

可以看到是没有源代码的。那么

  • 传说中java的class文件可以一次编译到处运行,那么源代码采用GBK还是UTF-8会有影响么?
  • 如果lib有2个不同版本的jar,例如spring2.5,spring3,还能安心干活么?
  • 如果classes有个class文件不小心被打到jar包去,遗忘在lib目录,以后更新classes会不会炸了?
  • log4j.xml放到conf目录会有问题么? 有什么区别没有?
  • 有人写了个Niubility的类放在yy.war, 为什么我就调用不到呢,明明同一个猫上跑的?
  • 听说有servlet3支持异步可厉害了,但放个demo到tomcat6会挂了,我lib明明有高大上的servlet-api.jar?
  • 听说web.xml里边可以配置监听器listener,但它监听什么?
  • 为什么不建议把jsp放在war的根目录下?

如果可以轻松无压力,那么说明java web基础还是可以的。

@首发于公司内部群组

一次INTERNAL_SERVER_ERROR的问题分析

问题现象

晚上版本上线后,发现工号进入首页后页面空白,显示INTERNAL_SERVER_ERROR

过程回顾

  1. 通过fiddler抓包,发现某个请求出现500错误
  2. 检查应用,was,ihs日志,没有发现有效日志
  3. 发现只有部分工号有问题,开始怀疑存在数据问题,准备导数据回测试环境验证
  4. 同时,新建一套ihs和was环境进行验证
  5. 新环境验证,发现原来失败的工号可以正常,怀疑环境配置有问题。同时发现请求超过4s就会返回500错误
  6. 尝试直接访问原was,发现是正常的
  7. 对比两个环境配置,was和ihs配置是一样的,但plugins-in参数ServerIOTimeout有差异,旧的是-1,新的为0
  8. 修改新的为-1,访问问题重现
  9. 准备修改为0,问题修复

技术分析

关于ServerIOTimeout参数的描述,可以参考以下链接。

取值为负数的情况,描述如下:

ServerIOTimeout value can be either positive or negative. If positive, when the ServerIOTimeout pops, the plug-in will not mark that server down. If negative, when the ServerIOTimeout pops, it will mark that server down. If your application uses HttpSession object, then there will be session affinity in play, so it would be best to choose a negative ServerIOTimeout value, to ensure that the retry will not be sent back to the same server that just timed-out. Since that server will be marked down, the retry will go to a different appserver in the cluster.

简单来说,关于参数的取值都是需要权衡的。ServerIOTimeout表示ihs和was之间连接的读写超时时间,取值有三种值:

  • 0 表示一直等待was响应,这样可能会造成长时间不响应,连接也不释放
  • 正数 表示读写超时时间,超时之后会重试,这样可能会造成业务重复受理
  • 负数 和正数一样,不过超时之后会认为当前was为下线状态,然后重试其他服务器,对于无状态的请求同样可能有重复受理的问题。在上面的问题中,ihs后面有4个was,所以刚好看上去就是超过4s会超时

小总结

  • 优化应用日志很重要,特别是那些没什么用的异常日志,需要定期优化现网日志
  • 最小化的可重现问题的环境
  • 熟悉常见调试工具,如fiddler,telnet,wget,curl,tcpdump等
  • 修改参数应该慎重,知道参数的含义,不要想当然。这次就是以为-1和0是没有区别