Skip to content

babyblue94520/polar-bear-cache

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

36 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Polar Bear Cache

Goal

  • 多服務架構下,依舊使用本地緩存
  • 減少第三方緩存服務的壓力
  • 減少網路 I/O 和建構物件的CPU消耗,提高效能
  • 相容 Spring Cacheable

設計

由於緩存通常應用在不頻繁異動的資料上,所以不應該每次都向第三方緩存服務取得資料,而是透過 MQ Service 廣播和訂閱訊息功能,發布註銷的事件,將對應的資料清除,大幅提高系統效能,降低龐大架構的維護成本

  • 由於緩存通常應用在不頻繁異動的資料上
  • 透過 Message Queue Server 發布和訂閱訊息功能,通知所有服務註銷本地的緩存

Usage

Single Service

SpringBootApplication

@EnablePolarBearCache
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

CacheConfig

public class CacheConfig {
  @Bean
  @Primary
  public PolarBearCacheManager polarBearCacheManager() {
      return new BasicCacheManager();
  }
}

Multi Service

The architecture of multiple services needs to send evict events to other services through EventService.

SpringBootApplication

@EnablePolarBearCache
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

CacheEventServiceImpl

Redis example.

public class CacheEventServiceImpl extends PolarBearCacheEventService implements InitializingBean {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedisMessageListenerContainer redisMessageListenerContainer;

    @Autowired
    private DefaultClientResources defaultClientResources;

    private final String topic = "cache";
    
    @Override
    public void afterPropertiesSet() throws Exception {
        defaultClientResources.eventBus().get().subscribe((event) -> {
            if (event instanceof ConnectedEvent) {
                publishConnectedEvent();
            }
        });
    }

    @Override
    public void send(String body) {
        stringRedisTemplate.convertAndSend(topic, body);
    }

    @Override
    public void addListener(Consumer<String> listener) {
        redisMessageListenerContainer.addMessageListener((message, pattern) -> {
            log.info("pattern:{},message:{}", new String(pattern), message);
            listener.accept(new String(message.getBody()));
        }, new PatternTopic(topic));
    }
}

CacheConfig

public class CacheConfig {
  @Bean
  public PolarBearCacheEventService polarBearCacheEventService() {
      return new CacheEventServiceImpl();
  }

  @Bean
  @Primary
  public PolarBearCacheManager polarBearCacheManager() {
      return new BasicCacheManager();
  }
}

使用方式

Reference Spring Cacheable

  • @Cacheable

    • 一般

      public class UserService {
        @Cacheable(
            cacheNames = "User"
            , key = "#id"
            , condition = "#id != null"
            , unless = "#result==null"
        )
        public User find(Integer id) {
            // TODO
        }
      }
    • 時效性

        import pers.clare.polarbearcache.annotation.CacheAlive;
      
        public class UserService {
          @CacheAlive("PT30S")
          @Cacheable(
              cacheNames = "User"
              , key = "#id"
              , condition = "#id != null"
              , unless = "#result==null"
          )
          public User find(Integer id) {
              // TODO
          }
      }
  • @CacheEvict

    如果有配置 Event Service 時,會將 Evict Event 廣播給其他使用相同配置的服務

    一般

    public class UserService {
      @CacheEvict(
          cacheNames = "User"
          , key = "#user.id"
          , condition = "#user.id != null"
      )
      public void update(User user) {
          //TODO
      }
    }
  • BeeCacheDependencies

    It also clears itself when dependency cache triggers the evict event.

    public class TestService implements InitializingBean {
    
      @Autowired
      private PolarBearCacheDependencies cacheDependencies;
    
      @Override
      public void afterPropertiesSet() {
        // Test cache  depends on user cache
        cacheDependencies.depend("Test", (key) -> {
            // Convert key
            return key;
        }, "User");
      }
    }
  • 註冊 Evict 的事件處理

    當本地有該緩存資源時,則會觸發 handler 取得最新資料,直接更新緩存,避免同時請求造成多次查詢或者鎖的等待。

    public class UserService implements InitializingBean {
      @Autowired
      private PolarBearCacheManager cacheManager;
    
      @Override
      public void afterPropertiesSet() {
        cacheManager.<User>onEvict(UserCacheKey, (key, data) -> find(data.getId()));
      }
    }