From bfd944ba4aeb9ad857355cf64bae58f59a6f4a3f Mon Sep 17 00:00:00 2001 From: qyx <565485304@qq.com> Date: Mon, 29 Aug 2022 16:52:18 +0800 Subject: [PATCH] =?UTF-8?q?[=E4=BB=A3=E7=A0=81=E9=87=8D=E6=9E=84](master):?= =?UTF-8?q?=20=E6=9B=B4=E6=96=B0=E4=BA=86=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Java避坑建议 --- code-language/java/README.md | 89 ++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/code-language/java/README.md b/code-language/java/README.md index 37d75de..91342d8 100644 --- a/code-language/java/README.md +++ b/code-language/java/README.md @@ -1574,6 +1574,65 @@ return orderDO; - TODO 借助这个话题来讨论设计模式在实际开发中的使用, 来优化我们的代码设计 ### 3.22 设计 - 接口设计:系统间对话的语言,一定要统一 +- 开发一个服务的第一步就是设计接口。接口的设计需要考虑的点非常多,比如 + - 接口的命名、参数列表、包装结构体、接口粒度、版本策略、幂等性实现、同步异步处理方式等。 +- 和接口设计相关比较重要的点有三个 + - 分别是包装结构体、版本策略、同步异步处理方式。 + +#### A. 接口的响应要明确表示接口的处理结果 +- 我曾遇到过一个处理收单的收单中心项目,下单接口返回的响应体中,包含了 success、code、info、message 等属性,以及二级嵌套对象 data 结构体。在对项目进行重构的时候,我们发现真的是无从入手,接口缺少文档,代码一有改动就出错。 +- 有时候,下单操作的响应结果是这样的:success 是 true、message 是 OK,貌似代表下单成功了;但 info 里却提示订单存在风险,code 是一个 5001 的错误码,data 中能看到订单状态是 Cancelled,订单 ID 是 -1,好像又说明没有下单成功。 +```json +{ +"success": true, +"code": 5001, +"info": "Risk order detected", +"message": "OK", +"data": { +"orderStatus": "Cancelled", +"orderId": -1 +} +} +``` +- 有些时候,这个下单接口又会返回这样的结果:success 是 false,message 提示非法用户ID,看上去下单失败;但 data 里的 orderStatus 是 Created、info 是空、code 是 0。那么,这次下单到底是成功还是失败呢? +```json +{ +"success": false, +"code": 0, +"info": "", +"message": "Illegal userId", +"data": { +"orderStatus": "Created", +"orderId": 0 +} +} +``` +- 这样的结果,让我们非常疑惑: + - 结构体的 code 和 HTTP 响应状态码,是什么关系? + - success 到底代表下单成功还是失败? + - info 和 message 的区别是什么? + - data 中永远都有数据吗?什么时候应该去查询 data? +- 造成如此混乱的原因是:这个收单服务本身并不真正处理下单操作,只是做一些预校验和预处理;真正的下单操作,需要在收单服务内部调用另一个订单服务来处理;订单服务处理完成后,会返回订单状态和 ID +- 为了将接口设计得更合理,我们需要考虑如下两个原则: + - 对外隐藏内部实现。虽然说收单服务调用订单服务进行真正的下单操作,但是直接接口其实是收单服务提供的,收单服务不应该“直接”暴露其背后订单服务的状态码、错误描述。 + - 设计接口结构时,明确每个字段的含义,以及客户端的处理方式。 +- + + + + + + + + + + + + + + + + ### 3.23 设计 - 缓存设计:缓存可以锦上添花也可以落井下石 @@ -2003,3 +2062,33 @@ wrk -t 10 -c 50 10s http://localhost:45678/redisvsmysql/mysql --latency - mongo数据库的使用建议 - 非重要数据,并且数据结构不固定的,插入量又很大的原始数据(比如爬虫原始数据)可以考虑Mongo。从个人喜好而言,综合性NOSQL,我更喜欢ES而不是Mongo,Mongo在数据量到TB级别我感觉不稳定,Sharding也不那么好用 +### 3.30 安全 - 如何正确保存和传输敏感数据 + + + + +### n. 总结 + +#### n.1 Java避坑建议 +- 1- 遇到自己不熟悉的新类,在不了解之前不要随意使用 + - CopyOnWriteArrayList。如果你仅仅认为CopyOnWriteArrayList 是 ArrayList 的线程安全版本,在不知晓原理之前把它用于大量写操作的场景,那么很可能会遇到性能问题。 + - 越普适的工具类通常用起来越简单,越高级的类用起来越复杂,也更容易踩坑。代码加锁这一讲中提到的,锁工具类 StampedLock 就比 ReentrantLock 或者 synchronized 的用法复杂得多,很容易踩坑。 +- 2- 尽量使用更高层次的框架 + - 偏底层的框架趋向于提供更多细节的配置,尽可能让使用者根据自己的需求来进行不同的配置,而较少考虑最佳实践的问题;而高层次的框架,则会更多地考虑怎么方便开发者开箱即用。 + - 谈到 Apache HttpClient 的并发数限制问题。如果你使用 Spring Cloud Feign 搭配 HttpClient,就不会遇到单域名默认 2 个并发连接的问题。因为,Spring Cloud Feign 已经把这个参数设置为了 50。 +- 3- 关注各种框架和组件的安全补丁和版本更新 + - 更新升级,以避免组件和框架本身的性能问题或安全问题带来的大坑。 +- 4- 尽量少自己造轮子,使用流行的框架 + - 如果我们自己去开发框架的话,很可能会踩一些别人已经踩过的坑。使用 Netty 开发 NIO 网络程序,不但简单而且可以少踩很多坑。 +- 5- 开发的时候遇到错误,除了搜索解决方案外,更重要的是理解原理 + - 只有知其所以然,才能从根本上避免踩坑。 +- 6- 网络上的资料有很多,但不一定可靠,最可靠的还是官方文档 + - 对于系统学习某个组件或框架,我最推荐的还是 JDK 或者三方库的官方文档。这些文档基本不会出现错误的示例,一般也会提到使用的最佳实践,以及最需要注意的点 +- 7- 做好单元测试和性能测试 + - 没有经过性能测试的代码,只能认为是完成了功能,还不能确保健壮性、可扩展性和可靠性。 +- 8- 做好设计评审和代码审查工作 + - 人都会犯错,而且任何一个人的知识都有盲区。每一段代码都能有至少三个人进行代码审核,就可以极大地减少犯错的可能性。 +- 9- 借助工具帮我们避坑 + - 使用工具来检测,就可以避免大量的低级错误 +- 10- 做好完善的监控报警 + - 基于合理阈值设置报警,那么可能就能在事故的婴儿阶段及时发现问题、解决问题。