1. 接口参数校验
2. 修改老接口时,注意接口的兼容性
3. 设计接口时,充分考虑接口的可扩展性
4. 接口考虑是否需要防重处理
5. 重点接口,考虑线程池隔离
6. 调用第三方接口要考虑异常和超时处理
7. 接口实现考虑熔断和降级
8. 日志打印好,接口的关键代码,要有日志保驾护航
9. 接口的功能定义要具备单一性
10. 接口有些场景,使用异步更合理
11. 优化接口耗时,远程串行考虑改并行调用
12. 接口合并或者说考虑批量处理思想
13. 接口实现过程中,恰当使用缓存
14. 接口考虑热点数据隔离性
15. 可变参数配置化,比如红包皮肤切换等
16. 接口考虑幂等性
接口幂等实现方案主要有 8 种:
-
select + insert + 主键/唯一索引冲突
-
直接 insert + 主键/唯一索引冲突
-
状态机幂等
-
抽取防重表
-
token 令牌
-
悲观锁
-
乐观锁
-
分布式锁
17. 读写分离,优先考虑读从库,注意主从延迟问题
18. 接口注意返回的数据量,如果数据量大需要分页
19. 好的接口实现,离不开 SQL 优化
-
explain 分析 SQL 查询计划(重点关注 type、extra、filtered 字段)。
-
show profile 分析,了解 SQL 执行的线程的状态以及消耗的时间。
-
索引优化(覆盖索引、最左前缀原则、隐式转换、order by 以及 group by 的优化、join 优化)
-
大分页问题优化(延迟关联、记录上一页最大 ID)
-
数据量太大(分库分表 、同步到 es,用 es 查询)
20. 代码锁的粒度控制好
21. 接口状态和错误需要统一明确
22. 接口要考虑异常处理
-
尽量不要使用
e.printStackTrace()
,而是使用log
打印。因为e.printStackTrace()
语句可能会导致内存占满。 -
catch
住异常时,建议打印出具体的exception
,利于更好定位问题。 -
不要用一个
Exception
捕捉所有可能的异常。 -
记得使用
finally
关闭流资源或者直接使用try-with-resource
。 -
捕获异常与抛出异常必须是完全匹配,或者捕获异常是抛异常的父类。
-
捕获到的异常,不能忽略它,至少打点日志吧。
-
注意异常对你的代码层次结构的侵染。
-
自定义封装异常,不要丢弃原始异常的信息
Throwable cause
。 -
运行时异常
RuntimeException
,不应该通过catch
的方式来处理,而是先预检查,比如:NullPointerException
处理。 -
注意异常匹配的顺序,优先捕获具体的异常。
23. 优化程序逻辑
24. 接口实现过程中,注意大文件、大事务、大对象
-
读取大文件时,不要
Files.readAllBytes
直接读取到内存,这样会 OOM 的,建议使用BufferedReader
一行一行来。 -
大事务可能导致死锁、回滚时间长、主从延迟等问题,开发中尽量避免大事务。
-
注意一些大对象的使用,可能会触发 fullGC。
25. 你的接口,需要考虑限流
26. 代码实现时,注意运行时异常(比如空指针、下标越界等)
27. 保证接口安全性
28. 分布式事务,如何保证
-
2PC(二阶段提交)方案、3PC
-
TCC(Try、Confirm、Cancel)
-
本地消息表
-
最大努力通知
-
seata
29. 事务失效的一些经典场景
-
方法的访问权限必须是 public,其他 private 等权限,事务失效。
-
方法被定义成了 final 的,这样会导致事务失效。
-
在同一个类中的方法直接内部调用,会导致事务失效。
-
一个方法如果没交给 spring 管理,就不会生成 spring 事务。
-
多线程调用,两个方法不在同一个线程中,获取到的数据库连接不一样的。
-
表的存储引擎不支持事务。
-
如果自己 try...catch 误吞了异常,事务失效。
-
错误的传播特性。
30. 掌握常用的设计模式
31. 写代码时,考虑线性安全问题
-
Hashmap、Arraylist、LinkedList、TreeMap 等都是线性不安全的。
-
Vector、Hashtable、ConcurrentHashMap 等都是线性安全的
32. 接口定义清晰易懂,命名规范
33. 接口的版本控制
34. 注意代码规范问题
35. 保证接口正确性,其实就是保证更少的 bug
36. 学会沟通,跟前端沟通,跟产品沟通