日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

通过READ-BEHIND CACHE来控制缓慢的生产者

發布時間:2023/12/3 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 通过READ-BEHIND CACHE来控制缓慢的生产者 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在我們的互聯世界中,我們經常使用我們不擁有或無權改善的API中的數據。 如果一切順利,他們的表現就會很好,每個人都會感到高興。 但是太多次,我們不得不使用延遲小于最佳延遲的 API。

當然,答案是緩存該數據 。 但是,您不知道何時過時的緩存是很危險的事情,因此這不是一個適當的解決方案。

因此,我們陷入困境。 我們需要習慣于等待頁面加載,或者投資一個非常好的微調器來招待用戶等待數據。 還是……是嗎? 如果為一個較小的,經過計算的折衷而又使用相同的緩慢生成器可以達到期望的性能,該怎么辦?

我想每個人都聽說過后寫式緩存。 它是高速緩存的一種實現,該高速緩存注冊了將異步發生的寫操作,在對后臺任務執行寫操作的同時,調用者可以自由地繼續其業務。

如果我們將這個想法用于問題的閱讀方面該怎么辦。 讓我們為慢速生產者提供一個后置緩存

合理警告 :此技術僅適用于我們可以在有限數量的請求中提供過時的數據。 因此,如果您可以接受您的數據將是“ 最終新鮮的 ”,則可以應用此數據。

我將使用Spring Boot來構建我的應用程序。 可以在GitHub上訪問所有提供的代碼: https : //github.com/bulzanstefan/read-behind-presentation 。 在實施的不同階段有3個分支。

代碼示例僅包含相關的行,以簡化操作。

現狀

分支機構:現狀

因此,我們將從現狀開始。 首先,我們有一個緩慢的生產者,它接收URL參數。 為了簡化此過程,我們的生產者將睡眠5秒鐘,然后返回一個時間戳(當然,這不是低變化數據的一個很好的示例,但是出于我們的目的,盡快檢測到數據是有用的) 。

public static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat( "HH:mm:ss.SSS" ); @GetMapping String produce(@RequestParam String name) throws InterruptedException { Thread. sleep (5000); return name + " : " + SIMPLE_DATE_FORMAT. format (new Date()); }

在消費者中,我們只是致電生產者:

//ConsumerController .java @GetMapping public String consume(@RequestParam(required = false ) String name) { return producerClient.performRequest(ofNullable(name).orElse( "default" )); } //ProducerClient .java @Component class ProducerClient { public String performRequest(String name) { return new RestTemplate().getForEntity( " http://localhost:8888/producer?name= {name}" , String.class, name) .getBody(); } }

簡單緩存

分支:簡單緩存

為了在Spring啟用簡單的緩存 ,我們需要添加以下內容

  • 依賴org.springframework.boot:spring-boot-starter-cache
  • 在application.properties中啟用緩存: spring.cache.type= simple
  • 將@EnableCaching注解添加到您的Spring Application主類
  • 將@Cacheable("cacheName")添加到要緩存的方法中

現在我們有一個簡單的緩存表示。 這也適用于分布式緩存 ,但是在此示例中,我們將堅持使用內存中的緩存。 使用者將緩存數據,并且在第一次調用后,等待時間消失了。 但是數據很快就會過時 ,沒有人將其逐出。 我們可以做得更好!

接聽電話

分行:碩士

我們需要做的下一件事是在發生呼叫時對其進行攔截,而不管是否將其緩存。

為了做到這一點,我們需要

  • 創建一個自定義注釋: @ReadBehind
  • 注冊一個方面,該方面將攔截以@ReadBehind注釋的方法調用

因此,我們創建了注釋并將其添加到performRequest方法

@ReadBehind @Cacheable(value = CACHE_NAME, keyGenerator = "myKeyGenerator" ) public String performRequest(String name) {

如您所見,定義了一個CACHE_NAME常量。 如果需要動態設置緩存名稱,則可以使用CacheResolver和配置。 同樣,為了控制密鑰結構,我們需要定義一個密鑰生成器。

@Bean KeyGenerator myKeyGenerator() { return (target, method, params) -> Stream.of(params) .map(String::valueOf) .collect(joining( "-" )); }

此外,為了添加方面,我們需要

  • 將依賴項添加到org.springframework.boot:spring-boot-starter-aop
  • 創建方面類
  • 我們需要實現Ordered接口并為getOrder方法返回1。 即使在值已經存在于高速緩存中時高速緩存機制將抑制方法的調用,方面也需要啟動
@Aspect @Component public class ReadBehindAdvice implements Ordered { @Before( "@annotation(ReadBehind)" ) public Object cacheInvocation(JoinPoint joinPoint) { ... @Override public int getOrder() { return 1; }

現在,我們可以攔截所有對@ReadBehind方法的調用。

記住電話

現在有了調用,我們需要保存所有需要的數據,以便能夠從另一個線程調用它。

為此,我們需要保留:

  • 被稱為
  • 參數調用
  • 方法名稱
@Before( "@annotation(ReadBehind)" ) public Object cacheInvocation(JoinPoint joinPoint) { invocations.addInvocation(new CachedInvocation(joinPoint)); return null; } public CachedInvocation(JoinPoint joinPoint) { targetBean = joinPoint.getTarget(); arguments = joinPoint.getArgs(); targetMethodName = joinPoint.getSignature().getName(); }

我們將這些對象保留在另一個bean中

@Component public class CachedInvocations { private final Set<CachedInvocation> invocations = synchronizedSet(new HashSet<>()); public void addInvocation(CachedInvocation invocation) { invocations.add(invocation); } }

我們將調用保持在一個集合中,并且我們有一個計劃的工作以固定的速率處理這些調用,這一事實將給我們帶來一個很好的副作用,即限制了對外部API的調用。

安排落后的工作

現在我們知道執行了哪些調用,我們可以開始計劃的作業以接聽這些調用并刷新緩存中的數據

為了在Spring Framework中安排工作,我們需要

  • 在您的Spring應用程序類中添加注釋@EnableScheduling
  • 使用@Scheduled注釋的方法創建作業類
@Component @RequiredArgsConstructor public class ReadBehindJob { private final CachedInvocations invocations; @Scheduled(fixedDelay = 10000) public void job() { invocations.nextInvocations() .forEach(this::refreshInvocation); } }

刷新緩存

現在我們已經收集了所有信息,我們可以對后讀線程進行真正的調用并更新緩存中的信息。

首先,我們需要調用real方法

private Object execute(CachedInvocation invocation) { final MethodInvoker invoker = new MethodInvoker(); invoker.setTargetObject(invocation.getTargetBean()); invoker.setArguments(invocation.getArguments()); invoker.setTargetMethod(invocation.getTargetMethodName()); try { invoker.prepare(); return invoker.invoke(); } catch (Exception e) { log.error( "Error when trying to reload the cache entries " , e); return null; } }

現在我們有了新數據,我們需要更新緩存

首先, 計算 緩存密鑰 。 為此,我們需要使用為緩存定義的密鑰生成器。

現在,我們擁有所有信息來更新緩存,讓我們獲取緩存參考并更新值

private final CacheManager cacheManager; ... private void refreshForInvocation(CachedInvocation invocation) { var result = execute(invocation); if (result != null) { var cacheKey = keyGenerator.generate(invocation.getTargetBean(), invocation.getTargetMethod(), invocation.getArguments()); var cache = cacheManager.getCache(CACHE_NAME); cache.put(cacheKey, result); } }

至此,我們完成了“隱藏式”想法的實施。 當然,您仍然需要解決其他問題。

例如,您可以執行此實現并立即在線程上觸發調用。 這樣可以確保在第一時間刷新緩存。 如果過時的時間是您的主要問題,則應該這樣做。

我喜歡調度程序,因為它也可以作為一種限制機制 。 因此,如果您一遍又一遍地進行相同的呼叫,則后讀調度程序會將這些呼叫折疊為一個呼叫

運行示例代碼

  • 先決條件:已安裝Java 11+
  • 下載或克隆代碼https://github.com/bulzanstefan/read-behind-presentation
  • 構建生產者: mvnw package or mvnw.bat package
  • 運行生產者: java -jar target\producer.jar
  • 構建使用者: mvnw package or mvnw.bat package
  • 運行使用者: java -jar target\consumer.jar
  • 訪問生產者: http:// localhost:8888 / producer?name = test
  • 訪問使用者: http:// localhost:8080 / consumer?name = abc
  • 使用者將在約15秒后(10秒調度程序,5 –新請求)返回更新后的值,但在首次呼叫后不應看到任何延遲 。

警告

就像我在本文開頭所說的那樣,在實現read-behind時您應該注意一些事情。

另外,如果您負擔不起最終的一致性 ,請不要這樣做

這適用于具有 低頻變化 API的高頻讀取

如果API實現了某種ACL ,則需要在緩存鍵中添加用于發出請求的用戶名。 否則,可能會發生非常糟糕的事情。

因此,請仔細分析您的應用程序,并僅在適當的地方使用此想法。

翻譯自: https://www.javacodegeeks.com/2019/12/take-control-your-slow-producers-read-behind-cache.html

總結

以上是生活随笔為你收集整理的通过READ-BEHIND CACHE来控制缓慢的生产者的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。