开发实践 / JAVA开发实践
content/practices/b-JAVA开发实践/z-开发实践简略版.md
- 工具库:apache、guava、hutool
- 调度任务框架:quartz/xxl-job/elastic-job(依赖中间件Zookeeper)
- 认证授权(第三方登录):jwt + shiro、spring security + jwt + oauth2.0、Sa-token
- 网络编程:netty(自定义二进制协议通信例如物联网(IoT)设备上报、金融交易系统、游戏服务器;自研API网关;即时通讯、在线文档协同多人编辑,实时同步等场景;自定义rpc框架;中间件开发等,常见于中间件开发的场景)
- Http框架:Feign(微服务内部调用)/ OkHttp
- redis:操作redis数据库,使用redisTemplate(建立jedis连接池);分布式服务框架redisson
- 消息组件:Kafka、RocketMQ、RabbitMQ
- 日志收集:slf4j框架(推荐),logback(日志处理,常用于elk日志系统)、Loki+grafana套件
- 监控系统:Dapper架构,Prometheus + Grafana
- 服务治理:sentinel(限流、熔断、降级)
- 云原生:服务网格lstio
- 鉴权实践:sa-token,前后端都需要做鉴权(如果不做鉴权处理,只通过页面配置用户按钮列表,但是用户仍然可以绕过页面直接请求后端接口);RBAC(role,user,permission/menu)
- RpcService封装远程服务调用的方法,返回 null 或者 response.getData()
- redis操作,使用Lua脚本串行执行命令:放到 Redis 服务器端一次完成,从而保证原子性与一致性,并减少来回网络开销。
- Lombok插件builder使用,优雅创建对象 xxx.builder().fileName().build()
- ReqDTO,针对不同接口创建不同的对象,使用 validate 进行校验
- 分布式ID构建,Leaf号段模式
- oss文件上传,策略模式,多种云oss
- Preconditions.checkArgument() 根据表达式判断,优雅抛出业务报错提示
- LoginUserContextHolder:登录用户上下文,使用ThreadLocal变量、设置feign拦截器,请求头增加LoginUserContextHolder.userId
- 缓存一致性:延迟双删,是一种缓存一致性方案,主要用来解决“更新数据库后,Redis 里缓存的旧数据怎么处理”的竞态问题。第一次删除先清空旧缓存,执行数据库更新,第二次使用MQ再次删除redis缓存(第一次先删了缓存,但是在真正把数据库更新完成之前,可能有其他请求来,这些请求会发现 Redis 没数据,于是就会去查数据库。如果此时数据库还没更新完,它查到的是旧数据,并可能把旧数据又写回 Redis,即缓存重建)
- elasticsearch实践,
- 事务消息处理事务:下游消费者收到消息时,数据库要保证数据是落库成功的(消息-本地事务一致性/最终一致性)
- data-align:数据对齐服务(增量采集(MQ) + 日增量去重落库 + 分片定时对齐 + 搜索索引重建)
- spring-security:密码加密和校验
- redis+Lua脚本:原子操作、热更新
- 订单系统:库存扣了,消息没发出去,订单不会创建,但库存已经少了,这就是”少卖”(库存丢失,货没了,钱没收到,公司亏钱;解决方案:定时任务发现不一致再补偿就行。)。商品库存已经为 0(或不足),但系统依然允许用户下单成功,导致卖出数量 > 实际库存数量,超卖现象(Redis 扣减逻辑漏洞 或 DB 最终校验失效,p0级事故,需联系用户取消订单or赔偿)。宁可少卖,也绝不能超卖。
- 订单系统超卖漏洞:Lua脚本逻辑漏洞;缓存与数据库不一致(redis库存扣减成功,MQ丢失);MQ消费者创建订单时,完全信任Redis的扣减结果,没有查询数据库校验最终库存
- 订单系统消息幂等处理:MQ消息可能被消费多次,消费者处理完还没ack就挂了,消息会重发。幂等处理:用请求ID或者订单号做唯一键,重复插入直接报错跳过。
- 接口幂等性(示例:用户创建订单,由于网络超时,用户以为失败了,又点了一次提交。没有幂等性: 系统创建了 2 个订单,用户被扣了 2 次钱。如果有幂等性: 系统识别出这是同一次请求,只创建 1 个订单,第二次请求直接返回“已创建”或忽略),解决方案:悲观锁(常用)、乐观锁(select version, update where version = '',如果version发生变化,effect rows为 0,被别人改过了,版本变了这时候代码捕获异常,需要重试)、唯一索引(必须做,弱幂等性)、去重表、分布式锁、token机制(前段请求接口前,获取token,接口进行redis删除验证)
- 防御性编程:不要假设上游一定是对的,每一层都要有自己的兜底逻辑
- 大列表数据DAO修改:最好精简字段,字段过多占导致数据包过大(大部分时间消耗在 数据包的拼装、网络传输负载以及数据库磁盘 I/O 上,最终影响程序的执行耗时)
- getBytes()函数,注意字符编码
- 前端交互反馈:看得见变不提示,看不见/重要/异步必提示;失败必提示,成功看感知。