【优化】-审批任务候选人提取超时
背景
低代码审批流在创建审批任务时需要为审批任务分配审批人,在配置审批人的时候,可以选择不同维度的身份,如用户、岗位、角色、组织,可以同时配置多个维度,每个维度的结果取交集。
在这个基础上还可以根据运行时环境不同选择不同组织下的用户,例如当前登录组织的上n级或者下n级,也可以配置多条,每条的结果取交集。
以上的配置可以配置多条,所有的配置取并集。
最开始暴露的问题是,在不选择任何维度和范围,查询全部人员作为候选人时,接口超时。
原始代码逻辑
- 为了统一逻辑,先根据身份id和类型查询出所有身份的信息
identity
,根据每个身份再查询出身份下的所有用户user
,每个身份下的用户列表取交集。 - 以上一步的结果为筛选条件,根据动态的环境条件和前一步得到的筛选条件查询出对应组织下的用户列表。
- 拿到每一条选人配置的结果,取并集。
要注意的是,有关用户查询的所有逻辑都在中台服务,由于调用的是中台提供的通用的dubbo接口,牵一发动全身,尽量不去改动中台的逻辑,优先改动自己服务的逻辑。
解决过程
遍历查转批量查
当选择的维度已经是用户的时候,不需要再根据类型为用户的identity
一个一个地查询user
,可以把identity
集合起来批量查询。
修改完之后依旧超时,因为用户量比较大,一次全部查出来仍然会dubbo接口超时。
异步分页查询
每次查询人员列表时先查询出人员总数,再根据总数异步分页查询用户列表,最后把结果合起来。其中用线程池控制线程最大数量。
其中比较重要的参数是每页的用户条数和线程池最大线程数:
- 每页记录数过大会导致返回时间变长,过短会导致调用接口次数变多,返回时间也会变长。经过多次测试和调整后,每页记录数定为了200。
- 线程池最大线程数过大会给中台的数据库过大的压力(测试过程中出现了中台服务崩溃的情况),过小会导致任务阻塞,接口响应变慢。经过多次测试和调整后,maximumPoolSize设置为了2,corePoolSize设置为了1。
这次优化完之后,接口响应时间到了10秒左右,没有超时了,但是依然慢得离谱。
业务逻辑优化
我发现查询全部人员时有一个多余的转换步骤,即先查询出identity
,再查询出user
,可以去掉第一步,直接查询出所有user
。
我在查询identity
的业务代码中加了一层判断,如果是查询全部人员,则在上下文中做一个标记,在做user
转换的地方去检查这个标记,就可以直接查询出所有user
。
做完这一步,性能确实有所提升,但是微乎其微,可能原本查询identity
的速度就不慢。
加缓存
这一步在当时被我当成终极优化!虽然在现在看来是个很蠢的事情。
既然每次查所有人都很慢,不如给这种类型的查询加一个缓存,把所有人的信息缓存起来,给缓存加一个合适的过期时间,当前设置了1小时,这样只有第一次查询会很慢,后续都能瞬间响应!
从某种角度来说这确实是很好的解决办法,但是这么多人员信息缓存在内存中非常浪费,很有可能导致OOM。
沟通中台进行优化
能优化的业务逻辑我都优化了,能异步的操作都异步了,接口还是很慢,怎么办!只能找中台优化一下他们的dubbo接口了。
我整理了我们系统遇到的问题和我的需求,就和中台的项目经理提出了增加接口的申请,我的诉求是增加一个返回内容精简且性能合格的接口,登记需求,等待任务分发并解决之后,我就把旧的中台接口换成了新的,结果非常完美,原本五秒的接口,现在可以在半秒就返回,几乎是无感知的延迟。
有好学的朋友就要问了,为什么中台能优化这么明显?
因为原本的接口是通用的,返回了很多不必要的用户信息,这就导致中台的代码里需要join各种表,甚至再去请求别的服务,去拿到如此多冗杂的数据,拖慢了性能,新的接口为我们的需求单独定制,只有我们需要的最基本的用户信息,所以很快就可以返回。
总结
这次优化有一些闭门造车,如果最开始就能看到问题的根源是因为中台接口的逻辑冗杂,就可以非常轻易地解决问题,也可以避免我在优化的过程中让业务代码的逻辑越来越复杂、越来越难以维护的问题。