专栏首页后端技术探索面向切面缓存设计

面向切面缓存设计

一、问题背景

在互联网行业,缓存作为一种家喻户晓的技术,在各个系统中起着提效降压的作用。但是缓存的引入,也使得处理逻辑变得复杂,尤其在当下微服务大行其道,一个大型系统动辄十几个模块,多人共同开发、维护的情况下,不同开发人员的缓存设计都不尽相同,并且多与业务代码紧密耦合。在这个背景下,本文着重思考一种统一的方案,借鉴面向切面的编程思想(AOP),实现面向切面的缓存设计,将系统中的缓存设计与主业务逻辑剥离开来。

二、常见的缓存设计分析

这是一个带有缓存的查询接口的时序图,相信这样的缓存设计大多数读者都实现过:

其流程图如下,这个带有缓存的查询接口涉及到了Redis的EXISTS、GET、SET等动作:

尽管这只是一个简单的带有缓存的查询接口,但是可以看到Redis的EXISTS、GET、SET这些动作将主业务逻辑(本例中是查询数据库)包围了起来,紧密的耦合在了一起。

到这里就应该抛出我们的问题了:

1、如果接口内的业务逻辑更加复杂,比如一个支持重入的下单接口,该如何设计缓存才能使耦合降低,使逻辑更清晰,代码的可读性更高?

2、如果有10个乃至100个这样复杂的接口,如何去简化开发缓存的工作量,复用之前的逻辑,避免大量重复代码?

答案就在面向切面编程的思想里。

三、面向切面缓存设计思路

何为切面?

可以理解为动态代理的实现,代理提供了在访问代理对象方法前后进行控制的支持,在这个过程中,代理类就是一个切面。

从面向切面编程的思想出发,一个与业务完全解耦、高复用性的缓存设计应该是一个缓存切面。

这里要

缓存切面的设计目的应该是:

1、将缓存设计和业务逻辑分离——将缓存设计放在切面中,将业务类加入切入点

2、切面内的缓存设计适用绝大多数的场景——从业务类中抽出高通用性的缓存设计,设计统一的缓存切面

3、功能可扩展性高——统一切面结合Java中的自定义注解实现功能增强

基于这些考虑因素,介绍设计缓存切面的主要思路:

首先是介绍缓存切面用到的注解技术(Annotation)。

相信熟悉Java的同学都会留意到,大多数的切面设计都与注解有关,对于缓存切面亦是如此。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface CacheKey {
    String fields() default "";
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface GetCache {
    /**
     * 失效时间
     * 默认300秒
     *
     * @return 过期时间
     */
    int expire() default 300;

    /**
     * 缓存开关
     *
     * @return cache switch
     */
    boolean isCache() default true;

    /**
     * key的前缀
     *
     * @return cache前缀
     */
    String keyPrefix() default "";

    /**
     * 是否需要对key做hash
     *
     * @return isHash
     */
    boolean isHash() default false;
}

CacheKey和GetCache是我们声明的两个用于标示组成缓存key的参数和开启缓存的方法的注解,GetCache还有许多的属性,用于功能的增强。

他们都有一个Target注解,说明了CacheKey和GetCache作用的地方,CacheKey作用于PARAMETER参数,而GetCache作用于METHOD方法。

下面是一个简单的使用例子,描述了一个带有缓存的订单查询接口:

public interface OrderService {
    @GetCache
    Order queryOrder(@CacheKey Long orderId);
}

在这个例子中,GetCache标示了queryOrder是一个带有缓存的接口,CacheKey标示了用于组成缓存key的参数orderId。

那么这两个注解是如何起作用的呢?这就要提到缓存切面里的具体设计了。

简化后的流程图如下:

从图上可以看出,GetCache在缓存切面中用于判断是否进入通用的缓存逻辑,而CacheKey则作为识别组成key的参数的依据。

在满足了基本的缓存需求后,我们可以通过注解上的属性设计开关,对一些业务的缓存设计进行增强。

上述的缓存设计主要面向的是被动缓存,由于缓存设计的统一和注解配置的灵活性,缓存切面可以往主动缓存、多级缓存等进行扩展,能得到很好的支持。

篇幅有限不一一展开,架构如下图:

最后来了解一下如何接入缓存切面。

SpringAOP的配置方式多样,在这里仅举一例。

首先需要依赖lib,在项目pom里进行依赖的配置:

<dependency>
    <groupId>com.baidu.trade</groupId>
    <artifactId>btc-library</artifactId>
    <version>1.0.1-SNAPSHOT</version>
</dependency>

然后是切面bean的注入,以及切入点的配置:(缓存切面使用MethodInterceptor进行实现)

<bean id="cacheMethodInterceptor" class="com.baidu.trade.library.cache.CacheMethodInterceptor"/>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    <property name="beanNames">
        <list>
            <value>orderService</value>
        </list>
    </property>
    <property name="interceptorNames">
        <list>
            <value>cacheMethodInterceptor</value>
        </list>
    </property>
</bean>
<bean id="btcLibraryCache" class="com.baidu.driver4j.bdrp.client.BdrpClientFactory">
    <property name="nodes" value="${btc.library.cache.redis.nodes}"/>
</bean>

最后就是在需要开启缓存的接口、接口参数上加上GetCache和CacheKey注解,可以参考上文的Annotation Demo

四、总结

本文从开发人员常用的缓存设计出发,展示了向面向切面编程技术的思路演变,提出了面向切面的缓存设计方案。作为一个纯技术的设计优化方案,缓存切面是高复用、高扩展、高避错、易使用的。

1、高复用主要体现在缓存切面里的缓存设计高度通用,使用缓存切面可以大大减少项目系统代码里重复的缓存设计,在开发和维护上节约人力成本。

2、高扩展则得益于自定义注解的支持,可以通过注解上的属性控制扩展内容,对于缓存的扩展性升级无须代码改动(只需要改动注解)。

3、高避错,指的是缓存切面可以有效避免由于不同开发人员,尤其是对缓存技术了解不够深入的初级程序员的编码问题导致的缓存设计缺陷,以及缓存设计缺陷带来的业务影响等。

4、易使用表现在接入缓存切面时的无代码侵入,接入方无须任何代码开发即可进行接入(只需要增加注解,配置)。

本文分享自微信公众号 - nginx(nginx-study)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-07-26

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Memcached的使用 一、安装和启动二、telnet操作memcached三、python操作memcached

             Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。它通过在内存中缓存数据和对象来减少读取数据库的次...

    zhang_derek
  • 数据库历险记(二) | Redis 和 Mecached 到底哪个好?

    说起缓存框架,我们最常用的缓存框架有 memcached、Redis 这两个,但它们之间其实是有差异的。

    陈树义
  • 谈乱序执行和内存屏障【转】

    10多年前的程序员对处理器乱序执行和内存屏障应该是很熟悉的,但随着计算机技术突飞猛进的发展,我们离底层原理越来越远,这并不是一件坏事,但在有些情况下了解一些底层...

    233333
  • 高并发请求的缓存设计策略

    前几天,我司出了个篓子。当时正值某喜闻乐见的关键比赛结束,一堆人打开我司app准备看点东西,结果从来没有感受到过这么多关注量的该功能瞬间幸福到眩晕,触发了熔断,...

    老白
  • 亿级请求下多级缓存那些事 转载

    摘要: 什么是多级缓存 所谓多级缓存,即在整个系统架构的不同系统层级进行数据缓存,以提升访问效率,这也是应用最广的方案之一。我们应用的整体架构如图1所示: 图1...

    zhangdd
  • WordPress浏览次数统计插件:WP-PostViews Plus

    wp-postviews-plus,该插件可以统计每篇文章的浏览次数,根据展示次数显示历史最热或最衰的文章排行、展示范围可以是全部文章和页面,并且启用插件后自动...

    zhangdd
  • 修改CentOS默认yum源为国内yum镜像源

    CentOS默认的yum源不一定是国内镜像,导致yum在线安装及更新速度不是很理想。这时候需要将yum源设置为国内镜像站点。国内主要开源的开源镜像站点是网易和阿...

    zhangdd
  • 数据库历险记(三) | 缓存框架的连环炮

    最近在思考数据库以及缓存的问题,发现这些知识点其实是有一点关联的,于是这篇文章通过一个连环提问的方式将这些知识点串联起来。

    陈树义
  • Flink on yarn初步讲解

    对于flink的基本概念和基本运行模式讲解的内容请参考这篇文章《Flink流式处理概念简介》。本文主要是讲解flink on yarn的运行原理及基本使用,后面...

    Spark学习技巧
  • 如何删除小程序缓存 / 小程序列表能同步吗 / 追剧小程序推荐 | 小程序问答 #11

    不知道有多少人在用微信谈工作?每次向对方用纯文字介绍自己的时候,都觉得低效又不美观。

    知晓君

扫码关注云+社区

领取腾讯云代金券