
业务开发常见问题
zypy
这个作者很懒,什么都没留下…
展开
-
函数式编程
函数式编程有很多种定义很多种特性,这里从架构的角度,只关注它的没有副作用和不修改状态。函数式编程中,函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。在架构领域所有的竞争问题、死锁问题、并发问题都是由可变变量导致的。如果有足够大的存储量和计算量,应用程序可以用事件溯源的方式,用完全不可变的函数式编程,只通过事务记录从头计算状态,就避免了前面提到的几个问题。要让一个软件系统完全没有可变变量是不现实的,但是可以通过将需要修改状态的部分和不需要修改的部分分隔成单独的原创 2021-02-06 19:54:59 · 167 阅读 · 0 评论 -
数据处理
客户端的计算不可信。虽然目前很多项目的前端都是富前端,会做大量的逻辑计算,无需访问服务端接口就可以顺畅完成各种功能,但来自客户端的计算结果不能直接信任。最终在进行业务操作时,客户端只能扮演信息收集的角色,虽然可以将诸如价格等信息传给服务端,但只能用于校对比较,最终要以服务端的计算结果为准。 所有来自客户端的参数都需要校验判断合法性。即使知道用户是在一个下拉列表选择数据,即使我们知道用户通过网页正常操作不可能提交不合法的值,服务端也应该进行参数校验,防止非法用户绕过浏览器 UI 页面通过工具直接向服务端提交原创 2021-01-31 20:32:59 · 243 阅读 · 0 评论 -
反射、注解和泛型
反射调用方法并不是通过调用时的传参确定方法重载,而是在获取方法的时候通过方法名和参数类型来确定的。遇到方法有包装类型和基本类型重载的时候。 反射获取类成员,需要注意 getXXX 和 getDeclaredXXX 方法的区别,其中 XXX 包括 Methods、Fields、Constructors、Annotations。这两类方法,针对不同的成员类型 XXX 和对象,在实现上都有一些细节差异,详情请查看官方文档。今天提到的 getDeclaredMethods 方法无法获得父类定义的方法,而 g...原创 2021-01-24 22:23:39 · 180 阅读 · 0 评论 -
时间类处理
使用 Java 8 中的日期时间包 Java.time 的类进行各种操作,会比使用遗留的 Date、Calender 和 SimpleDateFormat 更简单、清晰,功能也更丰富、坑也比较少。 在把 Date 转换为 LocalDateTime 的时候,需要通过 Date 的 toInstant 方法得到一个 UTC 时间戳进行转换,并需要提供当前的时区,这样才能把 UTC 时间转换为本地日期时间(的表示) 把 LocalDateTime 的时间表示转换为 Date 时,也需要提供时区,用于指...原创 2021-01-17 22:45:27 · 119 阅读 · 0 评论 -
缓存设计
缓存设计不能把诸如 Redis 的缓存数据库完全当作数据库来使用。不能假设缓存始终可靠,也不能假设没有过期的数据必然可以被读取到,需要处理好缓存的回源逻辑;而且要显式设置 Redis 的最大内存使用和数据淘汰策略,避免出现 OOM 的问题。 缓存的性能比数据库好很多,需要考虑大量请求绕过缓存直击数据库造成数据库瘫痪的各种情况。对于缓存瞬时大面积失效的缓存雪崩问题,可以通过差异化缓存过期时间解决;对于高并发的缓存 Key 回源问题,可以使用锁来限制回源并发数;对于不存在的数据穿透缓存的问题,可以通过布隆原创 2021-01-10 11:26:13 · 166 阅读 · 0 评论 -
敏感数据
敏感数据用户密码不能加密保存,更不能明文保存,需要使用全球唯一的、具有一定长度的、随机的盐,配合单向散列算法保存。 使用 BCrypt 算法,是一个比较好的实践。诸如姓名和身份证这种需要可逆解密查询的敏感信息,需要使用对称加密算法保存。把脱敏数据和密文保存在业务数据库,独立使用加密服务来做数据加解密 对称加密需要用到的密钥和初始化向量,可以和业务数据库分开保存 对于数据传输,则务必通过 SSL/TLS 进行传输。对于用于客户端到服务端传输数据的 HTTP,需要使用基于 SSL/TLS 的 HTTP原创 2021-01-03 23:07:54 · 650 阅读 · 1 评论 -
解决代码重复
解决代码重复利用工厂模式 + 模板方法模式,消除 if…else 和重复代码,有多个并行的类实现相似的代码逻辑。可以考虑提取相同逻辑在父类中实现,差异逻辑通过抽象方法留给子类实现。使用类似的模板方法把相同的流程和逻辑固定成模板,保留差异的同时尽可能避免代码重复。同时,可以使用 Spring 的 IoC 特性注入相应的子类,来避免实例化子类时的大量 if…else 代码。 使用硬编码的方式重复实现相同的数据处理算法。可以考虑把规则转换为自定义注解,作为元数据对类或对字段、方法进行描述,然后通过反射动态读原创 2020-12-27 21:26:37 · 949 阅读 · 0 评论 -
OOM
程序确实需要超出 JVM 配置的内存上限的内存。不管是程序实现的不合理,还是因为各种框架对数据的重复处理、加工和转换,相同的数据在内存中不一定只占用一份空间。针对内存量使用超大的业务逻辑,比如缓存逻辑、文件上传下载和导出逻辑,我们在做容量评估时,可能还需要实际做一下 Dump,而不是进行简单的假设。出现内存泄露,其实就是认为没有用的对象最终会被 GC,但却没有。GC 并不会回收强引用对象,可能经常在程序中定义一些容器作为缓存,但如果容器中的数据无限增长,要特别小心最终会导致 OOM。使用 WeakHash原创 2020-12-20 19:59:01 · 203 阅读 · 0 评论 -
2020-12-13
数值计算使用 BigDecimal 表示和计算浮点数,且务必使用字符串的构造方法来初始化 BigDecimal:要精确表示浮点数应该使用 BigDecimal。并且,使用 BigDecimal 的 Double 入参的构造方法同样存在精度丢失问题,应该使用 String 入参的构造方法或者 BigDecimal.valueOf 方法来初始化。 System.out.println(new BigDecimal("0.1").add(new BigDecimal("0.2"))); S.原创 2020-12-13 23:13:20 · 225 阅读 · 0 评论 -
异步处理
异步处理要考虑异步流程丢消息或处理中断的情况,异步流程需要有备线进行补偿。比如全量补偿方式,即便异步流程彻底失效,通过补偿也能让业务继续进行。 异步处理的时候需要考虑消息重复的可能性,处理逻辑需要实现幂等,防止重复处理。 微服务场景下不同服务多个实例监听消息的情况,一般不同服务需要同时收到相同的消息,而相同服务的多个实例只需要轮询接收消息。我们需要确认 MQ 的消息路由配置是否满足需求,以避免消息重复或漏发问题。 要注意始终无法处理的死信消息,可能会引发堵塞 MQ 的问题。一般在遇到消息处理失败的原创 2020-12-06 16:01:41 · 186 阅读 · 0 评论 -
null和空指针处理
null和空指针处理针对判空,通过 Optional 配合 Stream 可以避免大多数冗长的 if-else 判空逻辑,实现一行代码优雅判空。另外,要定位和修复空指针异常,除了可以通过增加日志进行排查外,在生产上使用 Arthas 来查看方法的调用栈和入参会更快捷。 业务系统最基本的标准是不能出现未处理的空指针异常,因为它往往代表了业务逻辑的中断,所以我建议每天查询一次生产日志来排查空指针异常,有条件的话建议订阅空指针异常报警,以便及时发现及时处理。 POJO 中字段的 null 定位,从服务端的原创 2020-11-28 11:15:10 · 477 阅读 · 0 评论 -
数据库连接
连接超时代表建立 TCP 连接的时间,读取超时代表了等待远端返回数据的时间,也包括远端程序处理的时间。 在解决连接超时问题时,要搞清楚连的是谁;在遇到读取超时问题的时候,要综合考虑下游服务的服务标准和自己的服务标准,设置合适的读取超时时间。此外,在使用诸如 Spring Cloud Feign 等框架时务必确认,连接和读取超时参数的配置是否正确生效。 因为 HTTP 协议认为 Get 请求是数据查询操作,是无状态的,又考虑到网络出现丢包是比较常见的事情,有些 HTTP 客户端或代理服务器会自动重试 Ge原创 2020-11-22 23:02:13 · 135 阅读 · 0 评论 -
数据相关
数据代码要从根本上、从最底层进行堵漏,尽量不要在高层框架层面做,否则堵漏可能不彻底。 堵漏要同时考虑进和出,不仅要确保数据存入数据库的时候进行了转义或过滤,还要在取出数据呈现的时候再次转义,确保万无一失。 除了直接堵漏外,还可以通过一些额外的手段限制漏洞的威力。比如,为 Cookie 设置 HttpOnly 属性,来防止数据被脚本读取;又比如,尽可能限制字段的最大保存长度,即使出现漏洞,也会因为长度问题限制黑客构造复杂攻击脚本的能力。敏感数据用户密码不能加密保存,更不能明文保存,需要使用全球唯原创 2020-11-15 22:47:12 · 312 阅读 · 0 评论 -
serialVersionUID
serialVersionUID序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化,将数据分解成字节流,以便存储在文件中或在网络上传输。有两种初始化方式:默认的1L,比如:private static final long serialVersionUID = 1L; 根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段private static final long serialVersionUID = -8940196742313994740L;serial原创 2020-08-23 22:33:39 · 177 阅读 · 0 评论