一个优秀的工程师和一个普通的工程师的区别, 不是满天飞的架构图, 他的功底体现在所写的每一行代码上.
-- 毕玄
1. 命名风格
[书摘] 类名用 UpperCamelCase 风格, 比如 DO/BO/VO/PO
[书摘] 方法用 lowerCamelCase 风格, 尽量是动词
小思考: 例如常用的
- 获取单个对象, getUserById
- 获取对象累不, listUserByGroupId
- 统计对象数量, countUserByClassId
- 新增, 删除, insert,delete,update 等
2. 常量定义
[书摘] 如果变量值仅在一个范围变化, 则用 enum 类型
小思考: 例如电商中订单状态, 用手指也能列出来. 所以一般定义个 OrderSatausEnum 搞定, 伪代码如下:
- public enum OrderSatausEnum {
- TO_PAY(1,"代付款"), PAID(2,"已付款") ...;
- private Integer orderStatus;
- private String orderSatausDes;
- OrderSatausEnum(Integer orderStatus,String orderSatausDes) {
- this.orderStatus = orderStatus;
- this.orderSatausDes = orderSatausDes;
- }
- // get set
- }
进一步, 如果订单某天要加个 "待拼团" 状态咋办? 所以这些配置化的东西, 可以上配置中心. 比如携程出的 apollo 等
3. OOP 规则
[书摘] Object 的 equals 方法容易抛空指针异常, 应使用常量或者确定优质的对象来调用 equals .
比如:"TO_PAY".equals(order.getOrderStatus()). 反着写就不对了, 因为 order.getOrderStatus() 可能为 null.
自然, 更加推荐 java.util.Objects#equals 工具类.
[书摘] 所有相同类型的包装类对象之间的值比较, 全部使用 equals 方法.
小思考: 别用 == 了. equals 也要注意些事情, 比如 Byte 类型的 status 对象值, 用 equals 要注意如下:
- getStatus().equals(0) // 反例, false
- getStatus().equals((byte)0) // 正例, true
还有, == 比如 Integer 在 -128 到 127 范围比较正常, 超过就不正常. 原因是 -128 到 127 范围的对象在 IntegerCache.cache 中产生, 会复用对象. 所以所以, 切记切记, 别用 == 了, 用 equals 去比较.
4. 集合处理
[书摘] 不要在 for 循环中进行元素的 remove/add 操作. remove 请使用 Iterator 方法, 如果有并发操作, 则对 Iterator 对象加锁.
具体 Iterator 怎么操作集合, 百度下即可. 这还是典型的 迭代器设计模式, 可以深入源码看看人家的简单实现原理, 又能学到一发高级知识.
5. 并发处理
[书摘] 第一, 线程必须通过线程池来提供, 不允许显式创建线程. 第二, 线程池不允许用 Executors 创建, 应使用 ThreadPoolExecutor 去创建. 因为
Executors 创建的几种 ThreadPool 会有弊端:
FixedThreadPool 和 SingleThreadPool 允许请求队列长度为 Integer.MAX_VALUE , 大量请求, 会导致 OOM
CachedThreadPool 和 ScheduledThreadPool 允许创建最大的线程数为 Integer.MAX_VALUE, 大量创建线程, 会导致 OOM
所以, 使用 ThreadPoolExecutor 的原因是能更好地理解线程池的运行规则, 规避资源耗尽, 更好地贴合某个业务场景, 去创建更适合的线程池.
[书摘] 在高并发场景中, 同步调用应该考虑锁的性能损耗. 能用无锁数据结构, 就不要用锁; 能锁区块, 就不要锁整个方法体; 能用对象锁, 就不要用类锁. 即, 加锁的粒度越小, 性能损耗越小. 并且避免锁的代码块中调用了 RPC 方法.
另外, 同时对多个资源加锁的时候, 需要保持一致的加锁顺序. 否则, 一个线程加锁顺序为 ABC, 另一个加锁顺序为 ACB 或 BAC 等, 会造成死锁. 如图:
[书摘] 金融资金相关信息, 使用悲观锁. 比如更新某个用户的钱包余额字段.
小思考: 我以前做 P2P 的时候, 就很简单地使用了 MySQL 的行锁.
SELECT * FROM xx WHREER xx.id=888 FOR UPDATE
具体行锁, 表锁大家可以自行百度了解.
6. 控制语句
[书摘] 高并发场景, 比如秒杀场景, 商品扣库存, 库存的判断不要用 "等于" 来判断商品库存已售罄的条件. 应使用大于或者小于的条件来代替.
小思考: 这是典型的超卖场景. 有人会问也会存在超卖几件的问题吧? 答案是是的. 但如果用 等于 来判断, 超卖的件数会很多很多, 比如达到 1 万件. 但超卖 1 万件和超卖 1 件是不一样等级的故障. 或者是一个故障和一个不是故障的区别.
7. 异常处理
[书摘] 异常不要用来做流程控制, 条件控制
小思考: 昨天京东小哥问我, 这个能这么搞降级吗? 如下代码:
- try {
- searchFromES()
- }catch(){
- searchFromDB()
- }
这不算降级, 这也不能这么搞. 第一, 代码这也写就不对, 异常不要用来做流程控制, 条件控制. 第二, 这个只要实现 ES 读取有问题, 读取不到就读 DB. 可以考虑责任链设计模式去实现. 伪代码如下:
- ESHandle {
- void handle() {
- try {
- searchFromES()
- }catch(){
- }
- }
- }
- DBHandle {
- void handle() {
- try {
- searchFromES()
- }catch(){
- }
- }
- }
- // 两个 Handle 利用责任链去实现即可.
8. 建表规约 ,SQL 语句
[书摘] 当单表行数超过 500 万行或者单表容量超过 2 GB 时, 才推荐进行分库分表.
如果预计三年后的数量级无法达到这个级别, 请不要在创建表时就分库分表.
[书摘] 不要使用 count(列名) 或者 count(常量) 来替代 count(*). 因为它是 SQL92 定义的标准统计行数的预发. 它会统计 NULL 的行.
[书摘] where 条件下里面的 in 能避免就避免, 要注意 in 里面的集合数量, 控制在 1000 个之内.
[书摘] 在代码中写分页查询, 如果 count 为 0 , 直接返回 空列表. 避免执行下面的分页语句.
9. 服务器
[书摘] 高并发服务器建议调小 TCP 协议的 time_wait 超时时间. Linux 修改 /etc/sysctl.conf 文件, 代码如下:
net.ipv4.tcp_fin_timeout = 30
[书摘] JVM 设置参数 -XX:+HeapDumpOnOutOfMemoryError. 让 JVM 碰到 OOM 的时候, 输出 dump 信息.
小思考: 这个很重要. 二者得保留事故服务器现场. 比如 OOM 了某个服务器, 则在 VIP 或者啥摘到该机器, 让该机器不再有请求进入. 然后去查看 dump 信息, 去排查 OOM 问题.
最后
感谢小册子《阿里巴巴 Java 开发手册》, 感觉不错. 至少其中有几点, 有目共睹的.
来源: https://www.cnblogs.com/Alandre/p/10642327.html