跳到主要内容

对外服缓存

提示

本篇内容是对外服相关的扩展。

本篇文档不是必读内容,但介绍了一种性能提升的技巧,建议在时间充裕时阅读。

介绍

我们对业务数据做缓存时,一般的做法是通过 Caffeinecache2kehcacheJetCache 等专业的缓存库,将业务数据缓存在逻辑服中,以实现对业务数据的缓存。

而对外服缓存,可以将一些热点的业务数据缓存在对外服中。 当用户每次访问相关路由时,会直接从对外服内存中取数据。 这样既能避免反复请求逻辑服,又能减少序列化(编码)业务数据,从而做到性能的超级提升。

当我们把对外服缓存与专业的缓存库做结合时,可以发挥更大的性能效果。 因为我们可以将热点数据缓存在对外服中,之后其他用户访问热点数据时, 就不需要去逻辑服中取数据,而是直接在对外服这一环节中就能得到数据了。


对外服缓存对性能有着巨大的提升,主要体现在几个方面

  1. 当用户访问缓存数据时,响应更快了,因为请求链更少了。
  2. 直接在对外服中取数据,无需将请求传递到逻辑服中,无需对业务数据做序列化操作。
  3. 避免请求传递到逻辑服中,节省系统资源。

特点

  1. 零学习成本
  2. 可快速响应用户请求。
  3. 简化了缓存的使用,即使没有在逻辑服中对这些业务数据做缓存, 只要在对外服配置好相关的路由缓存,就能达到缓存的效果。
  4. 减少请求传递,同时对外服缓存还可以减少请求的传递, 使得业务数据在对外服就能处理,而不需要经过逻辑服。
  5. 避免序列化操作,由于路由对应的业务数据是以 byte[] 类型缓存在对外服的, 所以从缓存中取得的业务数据,将不再需要序列化(编码)操作了。 简单点说,就是不需要将业务对象转换成 byte[] 类型了。
  6. 支持条件缓存,同一 action 支持不同的请求参数。
  7. 支持路由范围缓存配置

对比各解决方案

为了让开发者更好的理解对外服缓存的优势,这里先引入两个开发者常用的第三方解决方案来做对比。


与 Hazelcast、Redis 等第三方解决方案相比较

  • Redis 有一些额外的开销,比如序列化和反序列化数据、对数据进行压缩和解压缩、网络IO、网络波动等方面。 使用对外服缓存特性可以有效避免这些问题,并且性能是 Redis 的数百倍
  • Hazelcast 也有一些额外的开销,每个节点占用的内存几乎相同, 无论这些内存数据是否对你有用,都会强行占用你的内存。 如果其中一个节点占用了 1G 内存,那么其他节点也将占用 1G 内存。 每次数据发生变动时,都需要经过N个节点的序列化、N个节点的网络IO等操作。 对于不熟悉 Hazelcast 的开发者来说,安装、配置和调试 Hazelcast 集群可能会比较复杂,需要花费一定的时间和精力。

使用 Hazelcast、Redis 等第三方解决方案,缺点

  • 第三方解决方案需要你进行额外的安装(安装成本、机器资源占用成本)。
  • 在项目中引入第三方后,需要学习相关 API 的使用(学习成本)。
  • 需要你进行很多额外的工作,例如数据的存储、更新等(使用成本、维护成本)。

对比小结

最重要的是,开发者使用对外服缓存时,是无感知的(零学习成本)。

对外服缓存HazelcastRedis描述
高性能两者比 Redis 快数百倍
零安装成本
零学习成本
零使用成本
零维护成本
内存占用按需增加节点均等内存加载所有
序列化次数00N每次访问缓存时
网络 io 次数00N每次访问缓存时

使用场景

使用场景举例

  • 如一些配置数据信息 : 装备的配置、道具的配置... 等。
  • 排行榜,如果及时性要求不高的,比如能接受 1、5、10、N 分钟同步一次的这种延迟排行榜,且数据量不多的(百来条或几百条)。
  • 更多举例后续补充...

对外服缓存的适用场景

  • 热点数据
  • 请求较为频繁的,数据不常变动的。
提示

你可以将对外服看成一台另类的 "Nginx、OpenResty" 等服务器, 可为整体架构做一些前置抵挡、功能扩展等职责。

对外服缓存并不是用于取代 Hazelcast、Redis 之类的,只是在某些业务场景下会具备更大的优势。

缓存处理流程图

external_cache

缓存处理流程图说明

  • Player : 用户。
  • ExternalServer : 对外服。
  • request : 用户发送请求。
  • response : 对外服的响应数据。
  • cacheData : 缓存数据。检测到该请求有对应的缓存,将缓存结果发送给用户,否则到逻辑服中获取。

如何使用

对外服缓存的使用与路由访问权限控制类似, 如果你之前了解过这部分的内容,那么花几分钟就能上手了。

  • code 3,配置缓存 3-1
PresentKit.ifPresent(ExternalGlobalConfig.externalCmdCache, externalCmdCache -> {
// cache config
externalCmdCache.addCmd(3, 1);
});

当用户请求 3-1 时,如果在对外服中找到了缓存数据,就直接在对外服环节将数据响应给用户。

如果没有找到缓存数据,请求将被传递到逻辑服,由逻辑服的操作进行处理。 在操作完成后,逻辑服将返回结果给对外服,并将该结果保存到缓存中。 下次其他用户请求 3-1 时,就可以从缓存中获取结果,从而实现快速响应。

更多配置

CmdCacheOption

CmdCacheOption 是对外服的缓存配置对象,缓存配置对象可以让我们在对外服缓存上做更精细的控制。

  • code 3,缓存过期时间 1 小时。
  • code 4,缓存过期检测时间间隔 5 分钟。
  • code 5, 同一个 action 的缓存数量上限设置为 256 条。
CmdCacheOption createCmdCacheOption() {
return CmdCacheOption.builder()
.setExpireTime(Duration.ofHours(1))
.setExpireCheckTime(Duration.ofMinutes(5))
.setCacheLimit(256)
.build();
}
提示

即使不设置,框架默认也是这个配置,这里只是展示如何创建的缓存配置选项。

配置不同的 CmdCacheOption

  • code 4~5,创建并置默认的缓存配置项,之后添加的路由缓存都将使用这个缓存配置。
  • code 6,添加路由缓存 22-1,将使用默认的缓存配置。
  • code 8~14,创建一个新的缓存配置对象,并设置到 22-2 、22-3 的路由中。 (不会使用默认的配置,而是使用 optionCustom 这个缓存配置对象。)
...
void extractedExternalCache() {
PresentKit.ifPresent(ExternalGlobalConfig.externalCmdCache, externalCmdCache -> {
var defaultOption = createCmdCacheOption()
externalCmdCache.setCmdCacheOption(defaultOption);
externalCmdCache.addCmd(22, 1);

var optionCustom = CmdCacheOption.builder()
.setExpireTime(Duration.ofSeconds(30))
.setExpireCheckTime(Duration.ofSeconds(5))
.build();

externalCmdCache.addCmd(22, 2, optionCustom);
externalCmdCache.addCmd(22, 3, optionCustom);
});
}

CmdCacheOption createCmdCacheOption() {
return CmdCacheOption.builder()
.setExpireTime(Duration.ofHours(2))
.setExpireCheckTime(Duration.ofMinutes(10))
.setCacheLimit(128)
.build();
}

路由范围缓存配置

这里,我们添加了主路由为 2 的值,对外服会对主路由为 2 下所有子路由的数据进行缓存。 通过路由范围缓存,我们可以避免为每个路由做单独的配置。

举个例子,对于 2-1、2-2、2-N 等子路由,即使你没有为这些子路由配置相关的缓存,缓存仍然会生效。 子路由使用的缓存配置与主路由使用的缓存配置相同。

PresentKit.ifPresent(ExternalGlobalConfig.externalCmdCache, externalCmdCache -> {
externalCmdCache.addCmd(2);
});

Example Source Code

注意

该功能为企业级功能