共计 2109 个字符,预计需要花费 6 分钟才能阅读完成。
作者:fredalxin\
地址:https://fredal.xin/enhance-rp…
近来总是会有服务遇到 OOM 的状况,简略定位后发现 rpc 框架内存占用较多,看来是时候须要优化一波了。
占用内存收缩
首先咱们须要简略理解一下目前 rpc 框架的层次结构。
先从服务注册核心 zookeeper 的数据结构看,一个命名空间对应数个服务,而每个服务对应数个实例信息,咱们的 api 信息则是与实例配置一起放在实例信息的 body 外面的。
当咱们依据用户配置的订阅列表拉取服务信息的时候,会将所有的 api 信息一起拉下来,目前在不扭转存储粒度的状况下这点是无奈优化的。
依据在 serviceContainer 对象中配置的订阅列表从 zk 上拉取数据后,为每一个服务实例别离创立了 serviceProvider 对象,同时因为咱们拉取了这个服务实例的所有 api 信息,所以还为每一个 api 都创立了一个 apiInvoker 对象。
serviceProvider 对象是依据配置的订阅列表中的 vip 而创立的,所以是”按需创立的“。但 apiInvoker 对象确是拉取 zk 上这个服务下所有的 api 信息一股脑创立起来的,而大部分场景下兴许咱们仅仅只须要依赖其中少数几个 api 接口而已。
不顾实在的依赖需要,而始终都创立所有的 apiInvoker 对象,在微服务利用变多、需要多样化、调用关系复杂化、api 接口持续增长的同时,占用内存也会继续收缩,甚至呈现 oom 状况。
寻址迟缓
寻址迟缓的问题对于一般微服务客户端和 api 网关都存在,而 api 网关尤为重大。
咱们须要先理解一个实在的申请是怎么寻址,找到对应的内存中已创立好的 apiInvoker 的。
对于一般微服务客户端来说,咱们往往应用动静代理创立 api 接口代理对象,而代理 api 上往往会带注解应用相似服务惟一标识码来标识出这个 api 是属于哪个依赖服务的。有了这个服务 id 信息后,咱们能够间接从内存中准确的找到对应的 serviceProvider 对象。
有了 serviceProvider 对象后咱们还须要寻找 apiInvoker 对象,这里就没有这么不便了。初始化时在构建好每个 apiInvoker 对象后会顺次退出到前缀树 prefixTree 里去,而在寻址时候通过代理 api 上的 url 信息来对 prefixTree 里的所有节点进行匹配从而找到对应的 apiInvoker。抛开前缀树的一些实现细节来说,咱们相当于是须要遍历这个 serviceProvider 下的所有 apiInvoker 来进行匹配。
至于 api 网关就更惨了,因为申请是间接来自于内部的,不可能会带有服务标识码,咱们不晓得这个申请是属于哪个服务的,大多数状况下咱们须要遍历所有的 serviceProvider 对象,而后再遍历外面的所有 apiInvoker 对象去顺次匹配,直到匹配到为止。当然,这边如果接口设计规范,同一个服务都是对立前缀的话就好办很多,这就是另一个问题了。
从下面得悉,不论怎么样,咱们都须要遍历一个实例里的所有 api 信息去寻址。但理论场景上,兴许咱们只须要依赖其中的一两个接口而已。
服务援用优化
理解上述原理之后,服务援用方面的优化是顺其自然的。之前在申明服务援用的时候,只是到服务级别,即配置了服务的订阅列表。然而其实更正当的是须要到 api 接口级别的,即形容咱们到底须要依赖哪些接口。
那么 api 网关在配置订阅列表时,在配置服务订阅同时申明这个服务上面的接口依赖。网关在治理平台的界面上增加服务信息及服务上面的接口信息后存储至数据库,初始化时间接从数据库抓取依赖信息并创立革新过的的 serviceContainer 对象即可。
rpc:
reference:
com.qh.middleware.rpctest:
- /test/testJson:POST
- /test/testKryo:POST
对于一般微服务客户端,是不太可能去间接配置 api 订阅的。因为使用者应用的代理对象都是基于 class 粒度的,所以这里在配置服务订阅的同时还须要配置服务下订阅的 api 接口类的全限定类名,与 dubbo reference 相似。这样,在取到须要订阅的 class 后会去扫描外面的所有 api method 信息,并转换成相应的 api 订阅。
rpc:
reference:
com.qh.middleware.rpctest:
- com.qh.mdw.api.testApi
- com.qh.mdw.api.testApi2
这样,每个 serviceProvider 对象中只会按需创立形容过的 apiInvoker 对象,而不是 zk 上拉取的所有 api。这样 apiInvoker 对象的数量就会按理论状况缩小,服务内存占用缩小,网关和微服务客户端的寻址也都会快很多。
近期热文举荐:
1.600+ 道 Java 面试题及答案整顿 (2021 最新版)
2. 终于靠开源我的项目弄到 IntelliJ IDEA 激活码了,真香!
3. 阿里 Mock 工具正式开源,干掉市面上所有 Mock 工具!
4.Spring Cloud 2020.0.0 正式公布,全新颠覆性版本!
5.《Java 开发手册(嵩山版)》最新公布,速速下载!
感觉不错,别忘了顺手点赞 + 转发哦!