软件架构 - 服务层和持久层之间的缓存
SW-Architecture - Caching between service- and persistence layer
我正在开发一个文件系统,它使用 fuse 库和 jnr-fuse (https://github.com/SerCeMan/jnr-fuse) 作为 java 绑定来封装对 Amazon S3 存储桶的访问。
我的第一个版本正在运行,目前正在做一些代码清理和重构工作,试图将所有内容都纳入适当的多层架构中。
到目前为止我大概有以下几点:
- 前端:这是来自 jnr-fuse 的 FuseFileSystem 接口的实际实现。它对 jnr(本机)类型有一些依赖性,并且这些方法是 java 等同于 fuse 的 c 函数。
- 服务层:一个接口,具有来自前端层的所有文件系统方法的 "non-native-dependent" 版本,但不依赖于 jnr 或 fuse 任何东西。这个想法是,这也可以在其他情况下使用(例如,作为 S3 的 java.nio.FileSystem-API 实现的核心组件或任何其他需要 [=52= 的场景] 使 S3 以 "filesystem-ish" 方式访问,但不想通过 fuse 这样做,因此不想要所有 jnr 依赖项)
我目前苦苦挣扎的地方是持久层:由于与 S3 的所有通信实际上都是通过 http 完成的,因此我正在做一些相当多的缓存以减少流量并提高性能。
问题是缓存最适合的位置..
显然,实际的 DAO 不应该被任何类型的 caching/locking 逻辑污染——它们应该只处理对数据的实际访问(即对 S3 进行 http 调用)。
另一方面,服务层也不应该真正关心缓存(以防持久层及其缓存需求发生变化),所以我正在考虑执行以下操作之一:
- 使用 "doubled" 持久层:每个 DAO 实现两次:一个版本持有缓存并从缓存中获取数据。如果对象不在缓存中,我们委托给第二个 DAO,它实际获取对象(然后将其添加到缓存中)
- 引入一个单独的 "cache" 层,其接口与处理所有缓存要求并在必要时委托给持久层的实际持久层略有不同。
从服务层的角度来看,版本 1 将是更清晰的版本 - 使用缓存和不使用缓存之间没有任何区别,因为对持久层的所有调用都将针对相同的接口。另一方面,它还会将所有有关文件状态或 "lifecycle" 的逻辑(打开 -> read/write -> 关闭)传输到持久层。
版本 2 将管理 "cache" 层内文件的生命周期,我认为这将使整个过程对于代码新手来说更容易理解。另一方面,它还假设总是是一个缓存层(这可能是真的)。
从设计的角度来看,上述方法是否还有其他优点和缺点,或者是否有任何模式可以解决此类问题?
现在我宁愿选择选项 2,但听到一些意见真的很有趣。
你为什么反对在 DAO 中缓存?这一直是我缓存的理想场所。它是一个数据访问问题,因此进入数据访问层。有几次我为了方便使用了各种 AOP 实现,但 90% 的时间,我在 DAO 中实现缓存逻辑。
缓存本身并不存在于 DAO 中,它通常是它自己的接口,因此我可以在实现(内存中、磁盘上等)之间进行交换。
我在使用 Apache HTTP 客户端自己的内置缓存时也很幸运。它允许您尊重 HTTP 缓存语义,或使用自定义逻辑覆盖它。
我正在开发一个文件系统,它使用 fuse 库和 jnr-fuse (https://github.com/SerCeMan/jnr-fuse) 作为 java 绑定来封装对 Amazon S3 存储桶的访问。
我的第一个版本正在运行,目前正在做一些代码清理和重构工作,试图将所有内容都纳入适当的多层架构中。
到目前为止我大概有以下几点:
- 前端:这是来自 jnr-fuse 的 FuseFileSystem 接口的实际实现。它对 jnr(本机)类型有一些依赖性,并且这些方法是 java 等同于 fuse 的 c 函数。
- 服务层:一个接口,具有来自前端层的所有文件系统方法的 "non-native-dependent" 版本,但不依赖于 jnr 或 fuse 任何东西。这个想法是,这也可以在其他情况下使用(例如,作为 S3 的 java.nio.FileSystem-API 实现的核心组件或任何其他需要 [=52= 的场景] 使 S3 以 "filesystem-ish" 方式访问,但不想通过 fuse 这样做,因此不想要所有 jnr 依赖项)
我目前苦苦挣扎的地方是持久层:由于与 S3 的所有通信实际上都是通过 http 完成的,因此我正在做一些相当多的缓存以减少流量并提高性能。 问题是缓存最适合的位置..
显然,实际的 DAO 不应该被任何类型的 caching/locking 逻辑污染——它们应该只处理对数据的实际访问(即对 S3 进行 http 调用)。 另一方面,服务层也不应该真正关心缓存(以防持久层及其缓存需求发生变化),所以我正在考虑执行以下操作之一:
- 使用 "doubled" 持久层:每个 DAO 实现两次:一个版本持有缓存并从缓存中获取数据。如果对象不在缓存中,我们委托给第二个 DAO,它实际获取对象(然后将其添加到缓存中)
- 引入一个单独的 "cache" 层,其接口与处理所有缓存要求并在必要时委托给持久层的实际持久层略有不同。
从服务层的角度来看,版本 1 将是更清晰的版本 - 使用缓存和不使用缓存之间没有任何区别,因为对持久层的所有调用都将针对相同的接口。另一方面,它还会将所有有关文件状态或 "lifecycle" 的逻辑(打开 -> read/write -> 关闭)传输到持久层。 版本 2 将管理 "cache" 层内文件的生命周期,我认为这将使整个过程对于代码新手来说更容易理解。另一方面,它还假设总是是一个缓存层(这可能是真的)。
从设计的角度来看,上述方法是否还有其他优点和缺点,或者是否有任何模式可以解决此类问题?
现在我宁愿选择选项 2,但听到一些意见真的很有趣。
你为什么反对在 DAO 中缓存?这一直是我缓存的理想场所。它是一个数据访问问题,因此进入数据访问层。有几次我为了方便使用了各种 AOP 实现,但 90% 的时间,我在 DAO 中实现缓存逻辑。
缓存本身并不存在于 DAO 中,它通常是它自己的接口,因此我可以在实现(内存中、磁盘上等)之间进行交换。
我在使用 Apache HTTP 客户端自己的内置缓存时也很幸运。它允许您尊重 HTTP 缓存语义,或使用自定义逻辑覆盖它。