fredal的博客

专业修电脑,副业写代码

对RPC框架的内存优化—改变服务引用方式

RPC 158度 0评

近来总是会有服务遇到OOM的情况,简单定位后发现rpc框架内存占用较多,看来是时候需要优化一波了。

占用内存膨胀

首先我们需要简单了解一下目前rpc框架的层次结构。
bitc
先从服务注册中心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对象的数量就会按实际情况减少,服务内存占用减少,网关和微服务客户端的寻址也都会快很多。

使用基于SpringMVC的透明RPC开发微服务
Comments
Write a Comment