使用Spring Boot来使用Spring Data Redis

首先,先行动

添加依存关系

    • mavenの場合pomにspring-boot-starterとspring-boot-starter-redisを追加する。

 

    Spring Data デフォルトではJedisを利用してRedisにアクセスする。
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.3.0.M5</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-redis</artifactId>
    </dependency>
</dependencies>

设定

    • 起動クラスに@SpringBootApplicationをつけるだけでspring-boot-autoconfigureが自動的にRedisTemplateを利用可能にしてくれる。

 

    • デフォルトではlocalhost:6379に接続する。

 

    設定を変更したければapplication.ymlに以下のように記載、コネクションプールの設定なども記載可能。
spring:
    data:
        redis:
            host: 192.168.33.11
            port: 6379   

使用的方式

使用RedisTemplate操作Redis。

@Autowired
RedisTemplate redisTemplate;

public void write() {

    redisTemplate.opsForZSet().add(userId, itemId ,new Date().getTime()))

}

public List<History> read(long begin , long end) {
    return redisTemplate.opsForZSet().reverseRangeWithScores(null, begin , end))
            .stream().map(e -> {
             History history = new History();
             history.setDate( new Date((long)(e.getScore())));
             history.setItemId( result.getValue());
             return history;
    }).collect(Collectors.toList());
}
    • 上記はSortedSetを利用したサンプルです。SortedSetを利用することで以下のようなことが可能。

商品IDの重複を許可しないため、同じ商品を何度見られても1件として扱われる
2日前に商品Aを見た->1日前に商品Bを見た->今日商品Aをまた見た、の場合に商品Aを商品Bより優先する

将spring-context的缓存与之协同工作

我打算尝试将Redis用作缓存。

设定

    • CachingConfigurerSupportクラスを継承してCacheManagerの設定を追加する。

@EnableCachingアノテーションの付与も必要。

@Configuration
@EnableCaching
public class RedisConfiguration extends CachingConfigurerSupport {

    @Bean
    @Autowired
    public CacheManager cacheManager(RedisTemplate<Object,Object> redisTemplate>){

        RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate)

        // キャッシュ有効期限の設定(秒)
        Map<String, Long> expires = new HashMap<String, Long>();
        expires.put("cache.day", new Long(24 * 60 * 60));
        expires.put("cache.short", new Long(3 * 60));
        cacheManager.setExpires(expires);
        return cacheManager;
    }
}  

应用方式

    • Cacheableアノテーションを利用することでキャッシュに乗せることが可能。

 

    • value属性には上のexpiresに設定した値を指定すると専用の有効期限が設定される。

 

    • expiresに存在しない値にすると無期限(デフォルト)になる。

 

    key属性にはredisのキャッシュとして登録したいキーを記載する。EL式を記載することで引数の値を含んだキーとすることができる。
  @Cacheable(value = "cache.day", key = "'item/' #itemId")
  public Item find(String itemId) {
    // execute database sql
  }
    • この例ではvalue属性にcache.dayを利用していため有効期限(Redisのttl)が24時間となる。

 

    • 呼び出し元からfind(“001”)でコールしたときredisに登録されるkey値は「item/001」となる。

 

    item/001に対するvalueはsqlTemplate.find(itemId)の結果のオブジェクトであるItemをSerializeしたものになる。SerializerはRedisTemplateに指定することで変更可能。

内部操作

具有@Cacheable注解的方法将受到CacheInterceptor的处理。
下图显示了调用上述方法时的过程。

RedisCache.001.png
①EXISTS cache.day~lock

这是一种用于解锁的机制。
cache.day~lock是在执行RedisCache#clean处理时创建的键,只要该键存在,RedisCache就会等待。
虽然说等待,但其实只是在该键消失之前每300毫秒进行一次EXIST检查循环(在spring data redis 1.6.0.RELEASE中进行确认)。

②GET item/001
    • キー「item/001」に該当するデータを取得する。

 

    • 結果が存在しなければここで@Cacheableを付与したメソッドが呼び出される。空データでもキーが存在すればメソッドは呼び出されない。

 

    結果が存在してれば呼び出し元にRedisから取得した値を返却して終了。
④EXISTS cache.day~lock

在进行SET操作之前,与执行GET操作时一样,需要确认锁的状态。

⑤MULTI〜⑨EXEC
    • トランザクション開始します。ここから⑨EXECまでは同一トランザクションで処理される。

 

    • ⑥で値をセット。

 

    • ⑦ではkeyをcache.day~keysというSortedSetに登録。@Cacheableのvalue値毎にキーの一覧を管理する形になる。結果として有効期限毎にキーの一覧が管理されることになる。

 

    ⑧有効期限が設定されていればEXPIREコマンド発行。

清除缓存

@CacheEvictを付与したメソッドを呼ぶとDELする。
例えば以下のようにするとvalue属性を@Cacheableのvalueと同一にすることで対応するキャッシュを全て削除できる。

@CacheEvict(value = "cache.day", allEntries = true)
public void evict() {
    // delete DB data 
}

在Cache错误发生时,行为会发生变化。

通过将CacheErrorHandler注册为Bean,可以定义在发生与缓存相关的错误时的行为。

将Cache中的所有异常记录到日志中,无需让调用方意识到由于缓存而产生的错误,始终调用带有@Cacheable注解的方法的示例。

@Bean
@Override
public CacheErrorHandler errorHandler() {
  return new CacheErrorHandler() {
    @Override
    public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
      LOGGER.error(String.format("%s:%s:%s", cache.getName(), key, exception.getMessage()),
            exception);
    }

    @Override
    public void handleCachePutError(RuntimeException exception, Cache cache, Object key,
          Object value) {
      LOGGER.error(String.format("%s:%s:%s", cache.getName(), key, exception.getMessage()),
            exception);
    }

    @Override
    public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
        LOGGER.error(String.format("%s:%s:%s", cache.getName(), key, exception.getMessage()),
            exception);
      LOGGER.error(exception.getMessage(), exception);
    }

    @Override
    public void handleCacheClearError(RuntimeException exception, Cache cache) {
      LOGGER.error(String.format("%s:%s", cache.getName(), exception.getMessage()), exception);
      LOGGER.error(exception.getMessage(), exception);
    }
  };
}
广告
将在 10 秒后关闭
bannerAds