Java业务开发常见错误100例
朱晔
贝壳金服资深架构师
立即订阅
6045 人已学习
课程目录
已完结 39 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 业务代码真的会有这么多坑?
免费
代码篇 (20讲)
01 | 使用了并发工具类库,线程安全就高枕无忧了吗?
02 | 代码加锁:不要让“锁”事成为烦心事
03 | 线程池:业务代码最常用也最容易犯错的组件
04 | 连接池:别让连接池帮了倒忙
05 | HTTP调用:你考虑到超时、重试、并发了吗?
06 | 20%的业务代码的Spring声明式事务,可能都没处理正确
07 | 数据库索引:索引并不是万能药
08 | 判等问题:程序里如何确定你就是你?
09 | 数值计算:注意精度、舍入和溢出问题
10 | 集合类:坑满地的List列表操作
11 | 空值处理:分不清楚的null和恼人的空指针
12 | 异常处理:别让自己在出问题的时候变为瞎子
13 | 日志:日志记录真没你想象的那么简单
14 | 文件IO:实现高效正确的文件读写并非易事
15 | 序列化:一来一回你还是原来的你吗?
16 | 用好Java 8的日期时间类,少踩一些“老三样”的坑
17 | 别以为“自动挡”就不可能出现OOM
18 | 当反射、注解和泛型遇到OOP时,会有哪些坑?
19 | Spring框架:IoC和AOP是扩展的核心
20 | Spring框架:框架帮我们做了很多工作也带来了复杂度
设计篇 (6讲)
21 | 代码重复:搞定代码重复的三个绝招
22 | 接口设计:系统间对话的语言,一定要统一
23 | 缓存设计:缓存可以锦上添花也可以落井下石
24 | 业务代码写完,就意味着生产就绪了?
25 | 异步处理好用,但非常容易用错
26 | 数据存储:NoSQL与RDBMS如何取长补短、相辅相成?
安全篇 (4讲)
27 | 数据源头:任何客户端的东西都不可信任
28 | 安全兜底:涉及钱时,必须考虑防刷、限量和防重
29 | 数据和代码:数据就是数据,代码就是代码
30 | 如何正确保存和传输敏感数据?
不定期加餐 (6讲)
加餐1 | 带你吃透课程中Java 8的那些重要知识点(一)
加餐2 | 带你吃透课程中Java 8的那些重要知识点(二)
加餐3 | 定位应用问题,排错套路很重要
加餐4 | 分析定位Java问题,一定要用好这些工具(一)
加餐5 | 分析定位Java问题,一定要用好这些工具(二)
加餐6 | 这15年来,我是如何在工作中学习技术和英语的?
结束语 (2讲)
结束语 | 写代码时,如何才能尽量避免踩坑?
结课测试 | 关于Java业务开发的100个常见错误,你都明白其中缘由了吗?
Java业务开发常见错误100例
15
15
1.0x
00:00/00:00
登录|注册

加餐3 | 定位应用问题,排错套路很重要

朱晔 2020-04-09
你好,我是朱晔。
咱们这个课程已经更新 13 讲了,感谢各位同学一直在坚持学习,并在评论区留下了很多高质量的留言。这些留言,有的是分享自己曾经踩的坑,有的是对课后思考题的详细解答,还有的是提出了非常好的问题,进一步丰富了这个课程的内容。
有同学说,这个课程的案例非常实用,都是工作中会遇到的。正如我在开篇词中所说,这个课程涉及的 100 个案例、约 130 个小坑,有 40% 来自于我经历过或者是见过的 200 多个线上生产事故,剩下的 60% 来自于我开发业务项目,以及日常审核别人的代码发现的问题。确实,我在整理这些案例上花费了很多精力,也特别感谢各位同学的认可,更希望你们能继续坚持学习,继续在评论区和我交流。
也有同学反馈,排查问题的思路很重要,希望自己遇到问题时,也能够从容、高效地定位到根因。因此,今天这一讲,我就与你说说我在应急排错方面积累的心得。这都是我多年担任技术负责人和架构师自己总结出来的,希望对你有所帮助。当然了,也期待你能留言与我说说,自己平时的排错套路。

在不同环境排查问题,有不同的方式

要说排查问题的思路,我们首先得明白是在什么环境排错。
如果是在自己的开发环境排查问题,那你几乎可以使用任何自己熟悉的工具来排查,甚至可以进行单步调试。只要问题能重现,排查就不会太困难,最多就是把程序调试到 JDK 或三方类库内部进行分析。
如果是在测试环境排查问题,相比开发环境少的是调试,不过你可以使用 JDK 自带的 jvisualvm 或阿里的Arthas,附加到远程的 JVM 进程排查问题。另外,测试环境允许造数据、造压力模拟我们需要的场景,因此遇到偶发问题时,我们可以尝试去造一些场景让问题更容易出现,方便测试。
如果是在生产环境排查问题,往往比较难:一方面,生产环境权限管控严格,一般不允许调试工具从远程附加进程;另一方面,生产环境出现问题要求以恢复为先,难以留出充足的时间去慢慢排查问题。但,因为生产环境的流量真实、访问量大、网络权限管控严格、环境复杂,因此更容易出问题,也是出问题最多的环境。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Java业务开发常见错误100例》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(14)

  • hellojd 置顶
    我们线上k8s管理服务,有时候oom,服务重启由k8s触发的,这将导致设置的生成dump 文件无效。有好的思路吗?

    作者回复: 1、需要明白,xmx设置的堆只是java进程使用内存的一部分 https://stackoverflow.com/questions/53451103/java-using-much-more-memory-than-heap-size-or-size-correctly-docker-memory-limi

    所以你需要通过监控排查到底哪部分内存超限,但是heap的oom dump肯定是需要做的,并且配置-XX:NativeMemoryTracking=detail -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMTStatistics,需要的时候可以通过jcmd <pid> VM.native_memory summary/detail排查

    2、在排查清问题之前可以适当放开k8s的limit,以便你可以观察到内存增长的区域,方便排查问题

    3、可以和运维沟通一下,在oom killed的时候(oom killed应该会让pod状态变为unhealth,这个时候可以触发hook)能否做一下heapdump、jstack等,数据不要保存在容器里

    当然,像Darren的回复,在死之前做几次dump也是可以的

    2020-04-09
    5
  • Darren
    首先先试着回答下@hellojd童鞋的问题,不一定对,仅供参考:k8s应该在cpu或者内存的使用率上做报警,大于90%的时候可以dump和jstack一次,甚至jstat也可以做,然后95%的时候也同样执行一次,
    甚至98或者99的时候也可以做一次,这样不仅可以保留现场,同时还可以对比。可以更好的排查问题。

    说说遇到的问题吧,前几天刚刚发生的:上线了一个新功能,正常情况下没事,但是只要运行新业务,
    就发现内存的使用率慢慢升高,随后导致CPU的使用率升高,最后CPU的使用率大于90%,直接出发报警,最后导致服务挂了;
    在分析挂之前的dump的时候发现,JDBCClientImpl有几十万个,4G的dump文件中,JDBCClientImpl就占用了3G多,在分析jstat的文件,发现full GC特别频繁,但是回收效果并不明显,导致CPU飙升,因为使用的是vertx框架,connection是手动管理;
    新功能有一个情况是A方法获取connection,但是A方法内部也要调用B方法,就把connection传递给B方法,然后在B方法中关闭链接,但是B方法并不是每次都被调用,有if条件,当时是为了做健壮性判断的,现在导致不进B方法,导致数据库连接不释放,内存使用率飙升,full GC执行多次触发CPU报警。

    还有一个元数据区的问题,JDK8默认的元数据区大小是20.8M,因为class等都放在元数据去,当加载的calss文件多的时候,20.8M是不够的,只要元数据扩容,必定引起full GC,因此建议在启动的时候对于元数据区设定一个合适的大小。

    试着回答下问题:
    1、APP的问题就不回答了,因为没有APP的经验;
    2、目前我们的监控主要是(Springboot项目)spring boot actuator+Prometheus+Grafana;
    spring boot actuator监控jvm内部情况;
    自定义exporter采集硬件使用情况及容器内部使用情况,统一上报Prometheus,然后Grafana做显示。
    非Springboot项目,采用的是自研类似spring boot actuator的功能,暴露相关的metric,也是上报Prometheus。

    作者回复: 感谢分享

    2020-04-09
    7
  • 👽
    本文理解:

    应对出错其实主要就是三个阶段。

    1. before:保证留有合理的日志

    2. ing:提供及时的预警以及监控

    3. after:有充足的应对问题的应急预案,版本回滚,服务降级等

    关于个人的排错过程主要是线性的。端到端的一个过程。例如:A调B,B调C,C调D。我的做法是先看A和D,两个业务端是否正常。如果都正常,ABC的顺序挨个排查。检查A是否接收到返回结果,如果没有,则检查B是否接收到返回结果。比较粗暴,但在小型业务系统里,个人感觉处理也还好。

    对于与服务器不一样的问题,通常会模拟一份服务器数据,然后本地做压测模拟服务器流量。保证问题的复现,能在本地复现问题,排查基本上就不是什么难事了。这一思路在 唐扬 老师的《高并发系统设计40问》 中有提到,模拟服务器环境,其实就是尽量把流量,数据库等环境,尽量贴近服务器环境。

    作者回复: 总结的很好

    2020-04-09
    4
  • 👽
    如果你现在打开一个 App 后发现首页展示了一片空白,那这到底是客户端兼容性的问题,还是服务端的问题呢?如果是服务端的问题,又如何进一步细化定位呢?你有什么分析思路吗?

    首先,切换设备,或者模拟请求,查看服务是否正常访问。以排除客户端问题。

    确保服务端服务器状态,1确保正在运行,2资源占用处于正常状态,没有出现满载,3服务器可以被正常访问,排除网络问题。随后,备份即时的所有可能有需要的日志,条件允许尝试重启服务或者回滚,保证线上服务正常以及客户体验。时候根据遗留日志尝试进行排查。如果依然无果,模拟服务器环境和流量,进行Debug。



    对于分析定位问题,你会做哪些监控或是使用哪些工具呢?

    无论监控还是定位,对个人来说:

    首当其冲:Top,检查内存CPU占用情况等等。定期不定期检查一下服务正常运行,并确认资源占用情况。

    对于请求,个人的监控是,记录所有慢请求(处理时间过长的请求)。针对慢请求的情况分析,比如说,一个理应极快的请求出现了慢请求的情况就需要去分析,缓存问题,网络问题等等。

    曾经的项目,会监控服务状态,服务如果宕机了会给相应负责人发送短信通知排查。

    作者回复: 👍🏻

    2020-04-09
    3
  • 👽
    真不敢相信,如此高质量的内容,竟然只是选学的不定期加餐!!!

    作者回复: :)

    2020-04-09
    3
  • 海林
    内存的问题,搁以前我基本分析不出来

    作者回复: 后面有加餐会带你详细分析

    2020-04-16
    2
  • yinchi_516564
    分享几个遇到的运维犯的错误:
    1、现象:同一个请求有时候能查出结果,有时候返回为空
       原因:经排查是运维把应急时候用的服务器(直接返回200)误添加在了nginx代理中
    2、现象:同一笔请求有时候很快,有时候超时60s
       原因:运维路由规则配错导致 到mongodb 的去程和回程路径不一导致原来的mongo链接失效,client在默认情况下不会主动收回这些链接,当再次读写时就出现异常
    3、现象:同一笔请求有时候慢,有时候快
       原因:容器部署的服务有两个api实例假死,导致请求回源,拉低了接口整体响应的速度

    目前使用到的工具
    Grafana监控 主要做api接口监控
    Kibana监控 主要做日志监控
    听云Server 主要做服务器资源监控
    还会用到arthas及MAT分析工具

    最后,听老师的课程,涨了不少知识,日后写代码、分析问题有点点底气了,感谢老师无私的分享!

    作者回复: 👍🏻

    2020-04-09
    2
  • Husiun
    高质量内容,期待老师的更新;个人在实际应用中还主要是top定位,课后问题1个人没有相关经验,我的思路是先定位客户端问题再一步步排查到服务端,之后再top定位具体服务问题
    2020-04-09
    2
  • Geek_3b1096
    非常实用谢谢老师

    作者回复: 欢迎转发

    2020-04-19
    1
  • hanazawakana
    问题1,首先看下客户端的崩溃监控,再看下是不是只有这个机器有问题,如果不同机型也有问题,再看下是不是某个机器底层操作系统的问题,比如Android O,再看下是不是某个厂商操作系统的问题,如果是普遍存在的,考虑服务端问题,看下对应接口的日志有没有报错信息,如果没有日志,再分析下是不是nginx或者网络的问题
    2020-04-12
    1
  • 岳宜波
    我们各个环境都会开启一个调试实例,只有添加固定的cookie的时候请求才会打到调试实例上,可以在调试实例上远程调试,不影响其他用户使用

    作者回复: 不错

    2020-06-03
  • Asha
    老师 非常感谢你提供的关于直接存储差问题的文章 启发了很多。有两个问题,第一个是为什么我们已经知道是直接内存的问题了,还需要先去看堆内存呢 第二个是为什么在thread local中引用的直接内存在gc的时候也不会被释放 多谢

    作者回复: 这是文中作者的排查思路 你不一定需要按照这个思路来 tomcat的工作线程不会回收 引用的资源不会释放

    2020-04-26
  • Asha
    老师及同学们 我这发现了一个问题,背景是这样的 小伙伴每天定时redis缓存中的数据清空,然后重新加载。然后过一个月左右就会抛oom但是是direct memory out of memory. 之前猜测是不是redis客户端从服务端拿数据的时候 连接数太多了 后来看redis的客户端是有一个连接池管理的。烦请老师和同学们提供好的思路

    作者回复: 排查方式参考 https://coldwalker.com/2018/12//troubleshooter_directbytebuffer_memory_issue/

    2020-04-26
    1
  • Demon.Lee
    一是,程序逻辑有问题或外部依赖慢,使得业务逻辑执行慢,在访问量不变的情况下需要更多的线程数来应对。比如,10TPS 的并发原先一次请求 1s 可以执行完成,10 个线程可以支撑;现在执行完成需要 10s,那就需要 100 个线程。
    -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    每秒都来10请求,10秒就是100个请求,故需要100个线程,终于想明白了,我是真的好笨😭。
    老师的课破10000不是事吧,被低估了,我去部落吆喝吆喝。

    作者回复: 👍🏻

    2020-04-23
收起评论
14
返回
顶部