يدمج SPRING BOOT ذاكرة التخزين المؤقت لـ Spring ولديه تطبيقات متعددة من ذاكرة التخزين المؤقت ، مثل redis و jaffeine و jcache و ehcache ، وما إلى ذلك ، ولكن إذا استخدمت ذاكرة التخزين المؤقت واحدة فقط ، فسيكون لها استهلاك كبير في الشبكة (مثل redis) ، أو سيكون لها الكثير من استخدام الذاكرة (مثل CACHE CACHE ذاكرة تطبيق الكافيين). في العديد من السيناريوهات ، يمكن دمج ذاكرة التخزين المؤقت من المستوى الأول والثاني لتحقيق تحسن واسع في كفاءة المعالجة للتطبيق.
وصف المحتوى:
لفهم بسيط ، يتمثل ذاكرة التخزين المؤقت في قراءة البيانات من وسائط قراءة أبطأ ووضعها على متوسطة مع قراءة أسرع ، مثل Disk-> الذاكرة. عادة ما نقوم بتخزين البيانات على القرص ، مثل: قاعدة البيانات. إذا قرأتها من قاعدة البيانات في كل مرة ، فسيؤثر القرص نفسه على سرعة القراءة ، لذلك سيكون هناك ذاكرة التخزين المؤقت للذاكرة مثل redis. يمكنك قراءة البيانات خارجها ووضعها في الذاكرة ، بحيث عندما تحتاج إلى الحصول على البيانات ، يمكنك الحصول مباشرة على البيانات من الذاكرة وإعادتها ، والتي يمكن أن تحسن السرعة بشكل كبير. ومع ذلك ، يتم نشر redis بشكل عام بشكل منفصل في مجموعة ، لذلك سيكون هناك استهلاك على الشبكة IO. على الرغم من وجود أدوات لتجميع الاتصالات للربط بمجموعة Redis ، إلا أنه سيظل هناك بعض الاستهلاك في نقل البيانات. لذلك هناك ذاكرة التخزين المؤقت داخل التطبيق ، مثل: الكافيين. عندما تكون هناك بيانات تلبي المعايير في ذاكرة التخزين المؤقت للتطبيق ، يمكن استخدامها مباشرة دون الحاجة إلى الحصول عليها من خلال الشبكة لإعادة تشكيلها ، وبالتالي تشكيل ذاكرة التخزين المؤقت على مستوى على مستوى. يسمى ذاكرة التخزين المؤقت داخل التطبيق ذاكرة التخزين المؤقت من المستوى الأول ، ويسمى ذاكرة التخزين المؤقت عن بُعد (مثل redis) ذاكرة التخزين المؤقت من المستوى الثاني
ذاكرة التخزين المؤقت الربيع
عند استخدام ذاكرة التخزين المؤقت ، تكون العملية التالية هي ما يلي بشكل عام:
يمكن أن نرى من مخطط التدفق أنه من أجل استخدام ذاكرة التخزين المؤقت ، تمت إضافة الكثير من عمليات ذاكرة التخزين المؤقت بناءً على معالجة الأعمال الأصلية. إذا كانت هذه المقترنة مع رمز العمل ، فسيكون هناك الكثير من العمل المتكرر عند التطوير ، ولا يفضي إلى فهم الأعمال بناءً على الرمز.
SPRING CACHE هو مكون ذاكرة التخزين المؤقت المقدمة في حزمة سياق الربيع بناءً على التعليقات التوضيحية. يحدد بعض الواجهات القياسية. من خلال تنفيذ هذه الواجهات ، يمكن تحقيق ذاكرة التخزين المؤقت عن طريق إضافة التعليقات التوضيحية إلى الطريقة. سيؤدي ذلك إلى تجنب مشكلة رمز ذاكرة التخزين المؤقت المقترنة بمعالجة الأعمال. إن تنفيذ ذاكرة التخزين المؤقت لـ Spring هو امتداد لتغليف واجهة Method (MethodInterceptor) في الربيع AOP. بالطبع ، يتم تنفيذ Spring AOP بناءً على الجانب.
هناك واجهتان أساسيتان من ذاكرة التخزين المؤقت الربيع: ذاكرة التخزين المؤقت و cachemanager
واجهة ذاكرة التخزين المؤقت
توفير عمليات ذاكرة التخزين المؤقت المحددة ، مثل وضع ذاكرة التخزين المؤقت والقراءة والتنظيف. التطبيقات التي يوفرها إطار الربيع هي:
باستثناء Rediscache ، التي تقع في حزمة الربيع-Data-REDIS ، يوجد الآخرون بشكل أساسي في حزمة دعم السياق النابض.
#cache.javapackage org.springframework.cache ؛ استيراد java.util.concurrent.callable ؛ ذاكرة التخزين المؤقت للواجهة العامة {// cachename ، اسم ذاكرة التخزين المؤقت. في التنفيذ الافتراضي ، يمرر Cachemanager Cachename عند إنشاء Bean Cache. سلسلة getName () ؛ // احصل على ذاكرة التخزين المؤقت الفعلية ، مثل: redistemplate ، com.github.benmanes.caffeine.cache.cache <object ، Object>. لم أجد الاستخدام الفعلي بعد. قد أقوم فقط بتوفير الفاصوليا التي تحصل على ذاكرة التخزين المؤقت الأصلية بحيث تحتاج بعض عمليات ذاكرة التخزين المؤقت أو الإحصاءات إلى تمديدها. كائن getNativeCache () ؛ // احصل على قيمة ذاكرة التخزين المؤقت من خلال المفتاح ، لاحظ أن القيمة التي تم إرجاعها هي ValuewRapper. من أجل أن تكون متوافقة مع القيم الخالية ، يتم لف قيمة الإرجاع في طبقة ، ويتم الحصول على القيمة الفعلية من خلال طريقة الحصول على GET ValuewRapper GET (مفتاح الكائن) ؛ // احصل على قيمة ذاكرة التخزين المؤقت من خلال المفتاح ، الذي يقوم بإرجاع القيمة الفعلية ، أي نوع قيمة الإرجاع للطريقة <T> T GET (مفتاح الكائن ، فئة <T> النوع) ؛ // احصل على قيمة ذاكرة التخزين المؤقت من خلال المفتاح ، يمكنك استخدام valueloader.call () للاتصال بالطريقة باستخدام التعليق التوضيحي القابل للآمال. استخدم هذه الطريقة عندما يتم تكوين سمة Sync للشروح القابل للشرح إلى True. لذلك ، يجب ضمان مزامنة مصدر الإرجاع إلى قاعدة البيانات ضمن الطريقة. تجنب كمية كبيرة من الطلبات للعودة إلى المصدر إلى قاعدة البيانات عند فشل ذاكرة التخزين المؤقت. <t> t get (مفتاح الكائن ، قابل للاتصال <T> valueloader) ؛ // ضع البيانات التي يتم إرجاعها بواسطة طريقة التعليقات التوضيحية القابلة للتشغيل في وضع الفراغ ذاكرة التخزين المؤقت (مفتاح الكائن ، قيمة الكائن) ؛ // ضع ذاكرة التخزين المؤقت فقط عندما لا يكون هناك مفتاح في ذاكرة التخزين المؤقت. قيمة الإرجاع هي البيانات الأصلية عندما يكون المفتاح موجودًا في ValuewRapper putifabsent (مفتاح الكائن ، قيمة الكائن) ؛ // حذف ذاكرة التخزين المؤقت باطل إخلاء (مفتاح الكائن) ؛ // حذف جميع البيانات في ذاكرة التخزين المؤقت. تجدر الإشارة إلى أنه في التنفيذ المحدد ، يتم حذف جميع البيانات المخبأة فقط باستخدام التعليقات التوضيحية القابلة للتطبيق ، ولا تؤثر على ذاكرة التخزين المؤقت الأخرى في تطبيق Application Clear () ؛ // مغلفة واجهة إرجاع ذاكرة التخزين المؤقت ValuewRapper {// إرجاع كائن كائن مخبوق فعلي GET () ؛ } // عندما يتم إلقاء استثناء بواسطة {link #get (كائن ، قابل للاتصال)} ، سيتم لفه كاستثناء يتم إلقاؤه بواسطة suppressWarnings ("Serial") class valueretrivalexception يمتد Runtimeexception {مفتاح الكائن النهائي الخاص ؛ publueretrivalexception (مفتاح الكائن ، القابل للاتصال <؟> loader ، رمي Ex) {super (string.format ("القيمة للمفتاح '٪ s' لا يمكن تحميلها باستخدام '٪ s'" ، المفتاح ، المحمل) ، ex) ؛ this.key = المفتاح ؛ } الكائن العام getKey () {return this.key ؛ }}}واجهة Cachemanager
إنه يوفر بشكل أساسي إنشاء حبوب تنفيذ ذاكرة التخزين المؤقت. يمكن لكل تطبيق عزل ذاكرة التخزين المؤقت من خلال cachename ، ويتوافق كل اسم cachename مع تطبيق ذاكرة التخزين المؤقت. يظهر التنفيذ الذي يوفره إطار الربيع وتنفيذ ذاكرة التخزين المؤقت في الأزواج ، كما أن بنية الحزمة موجودة أيضًا في الشكل أعلاه.
#cachemanager.javapackage org.springframework.cache ؛ استيراد java.util.collection ؛ الواجهة العامة cachemanager {// إنشاء حبة تنفيذ ذاكرة التخزين المؤقت عبر cachename. يحتاج التنفيذ المحدد إلى تخزين حبوب تنفيذ ذاكرة التخزين المؤقت التي تم إنشاؤها لتجنب الإنشاء المتكرر ، وأيضًا تجنب الموقف الذي يتم فيه فقد محتوى ذاكرة التخزين المؤقت الأصلي بعد أن يتم إعادة إنشاء كائن ذاكرة التخزين المؤقت للذاكرة (مثل الكافيين). // إرجاع جميع مجموعة cachename <string> getCachenames () ؛}التعليقات التوضيحية المشتركة
cachable: يتم تطبيقه بشكل أساسي على طريقة الاستعلام عن البيانات
Package org.springframework.cache.annotation ؛ import java.lang.annotation.documented ؛ import java.lang.annotation.elementtype ؛ import java.lang.annotation.inherited ؛ java.lang.annotation.treation java.lang.annotation.target ؛ استيراد java.util.concurrent.callable ؛ استيراد org.springframework.core.annotation.Aliasfor ؛@target ({elementtype.method ، elementtype.type}) strenking Cachenames ، Cachemanager ينشئ حبة تنفيذ ذاكرة التخزين المؤقت المقابلة من خلال هذا الاسم aliasfor ("cachenames") سلسلة [] value () افتراضي {} ؛ aliasfor ("value") string [] cachenames () default {} ؛ // مفتاح ذاكرة التخزين المؤقت ، يدعم تعبيرات Spel. الافتراضي هو كائن ملفوف بجميع المعلمات و hashcode (SimpleKey) string key () الافتراضي "" ؛ // Cache Key Generator ، التنفيذ الافتراضي هو SimpleKeyGenerator String KeyGenerator () افتراضي "" ؛ // حدد أي Cachemanager لاستخدام String Cachemanager () الافتراضي "" ؛ // Cache Parser String Cacheresolver () افتراضي "" ؛ // حالة ذاكرة التخزين المؤقت ، تدعم بيانات التعبير SPEL ، وبيانات ذاكرة التخزين المؤقت فقط عند استيفاء الشروط المرضية. سيتم الحكم على شرط السلسلة () الافتراضي "" قبل وبعد استدعاء الطريقة ؛ // لم يتم تحديث ذاكرة التخزين المؤقت عند استيفاء الشرط ، يتم دعم تعبير SPEL ، وسلسلة ما لم يتم الحكم على () فقط "" بعد استدعاء الطريقة ؛ // عند العودة إلى المصدر إلى الطريقة الفعلية للحصول على البيانات ، سواء كنت متزامنًا؟ إذا كان خطأ ، فإن طريقة cache.get (مفتاح) تسمى ؛ إذا كان ذلك صحيحًا ، فإن طريقة cache.get (المفتاح ، القابلة للاتصال) تسمى boolean sync () الافتراضي الخاطئة ؛} CacheeVict: Clear Cache ، يتم تطبيقه بشكل أساسي على طريقة حذف البيانات. هناك خصائصان آخران من القابلية للتخزين المؤقتة
Package org.springframework.cache.annotation ؛ import java.lang.annotation.documented ؛ import java.lang.annotation.elementtype ؛ import java.lang.annotation.inherited ؛ java.lang.annotation.treation java.lang.annotation.target ؛ استيراد org.springframework.core.annotation.aliasfor ؛ target ({elementType.Method ، elementType.type})@entrention (attreentionpolicy.runtime) الموروثة@interface cacheevict { // ما إذا كنت تريد مسح جميع البيانات المخزنة مؤقتًا ، يتم استدعاء طريقة cache.vict (المفتاح) عند خطأ ؛ عندما يكون صحيحًا ، تسمى طريقة cache.clear () الافتراضية المنطقية () الافتراضية ؛ .تم دمج ذاكرة التخزين المؤقت لـ Spring في SPRING BOOT وتوفر مجموعة متنوعة من تكوينات ذاكرة التخزين المؤقت. عند استخدامه ، تحتاج فقط إلى تكوين ذاكرة التخزين المؤقت (enum cachetype) التي يجب استخدامها.
هناك تمديد إضافي تم إضافته إلى SPRING BOOT ، وهو واجهة CachemanagerCustomizer. يمكنك تخصيص هذه الواجهة ثم إجراء بعض الإعدادات لـ CacheManager ، مثل:
package com.itopener.demo.cache.redis.config ؛ import java.util.map ؛ import java.util.concurrent.concurrenthashmap ؛ import org.springframework.boot.autoconfigure.cache.cachemanagustomizer ؛ org.springframework.data.redis.cache.rediscachemanager ؛ الطبقة العامة إعادة تخصيص CACHEMANAGERCUSTOMIZER <DREDISCACHEMANAGER> {Override public void. cachemanager.setuseprefix (false) ؛ خريطة <string ، long> الانتهاء = concurrenthashMap الجديد <string ، long> () ؛ Expires.put ("useridCache" ، 2000l) ؛ cachemanager.setExpires (انتهاء صلاحية) ؛ }}تحميل هذه الفول:
package com.itopener.demo.cache.redis.config ؛ استيراد org.springframework.context.annotation.bean ؛ استيراد org.springframework.context.annotation.configuration ؛/** * @author fuwei.deng * @december 22 ، 2017 في 10:24 cacheredisconfiguration {bean public rediscacheManagerCustomizer rediscacheManagerCustomizer () {return New RediscachemanagerCustomizer () ؛ }}ذاكرة التخزين المؤقت شائعة الاستخدام هي redis. يقوم Redis بتنفيذ واجهة ذاكرة التخزين المؤقت Spring في حزمة Dring-Data-Redis.
فيما يلي بعض أوجه القصور في تنفيذ Rediscache ، أعتقد:
1. في الوقت الحالي عندما تفشل ذاكرة التخزين المؤقت ، إذا حصل مؤشر ترابط على بيانات ذاكرة التخزين المؤقت ، فقد يعود فارغًا. والسبب هو أن الخطوات التالية موجودة في تنفيذ Rediscache:
لذلك ، عندما تفشل ذاكرة التخزين المؤقت بعد الحكم على وجود المفتاح ، ومن ثم الحصول على ذاكرة التخزين المؤقت ليس له بيانات ، فإنه يعيد فارغًا.
2. السمة (القيم Cachenullvalues) المسموح لها بتخزين القيم الفارغة في rediscachemanager خاطئة بشكل افتراضي ، أي أنه لا يُسمح بتخزين القيم الخالية ، مما سيخاطر باختراق ذاكرة التخزين المؤقت. العيب هو أن هذه الخاصية من النوع النهائي ، ولا يمكن إنشاء الكائن إلا عند إنشاء الكائن من خلال طريقة المنشئ. لذلك ، لتجنب تغلغل ذاكرة التخزين المؤقت ، يمكنك فقط إعلان BENTISCACHEMANAGER BEAN في التطبيق.
3. لا يمكن تكوين الخصائص في إعادة تمييز CreadisChemanager مباشرة من خلال ملفات التكوين. لا يمكن تعيينها إلا في واجهة CachemanagerCustomizer. أنا شخصياً أعتقد أنه ليس مناسبًا.
الكافيين هو ذاكرة التخزين المؤقت للذاكرة عالية الأداء تعتمد على مفهوم تصميم الجوافة مفتوح المصدر من Google. تم تطويره باستخدام Java 8. بعد أن تم تقديم Caffeine Boot ، تم التخلي عن تكامل الجوافة تدريجياً. رمز مصدر الكافيين وعنوان مقدمة: الكافيين
يوفر Caffeine مجموعة متنوعة من استراتيجيات ملء ذاكرة التخزين المؤقت واستراتيجيات إعادة تدوير القيمة ، ويتضمن أيضًا إحصائيات مثل زيارات ذاكرة التخزين المؤقت ، والتي يمكن أن توفر مساعدة كبيرة في تحسين ذاكرة التخزين المؤقت.
لإدخال الكافيين ، يرجى الرجوع إلى: http://www.vevb.com/article/134242.htm
هنا نتحدث بإيجاز عن الأنواع التالية من استراتيجيات إعادة التدوير القائمة على الكافيين:
ذكرت في البداية أنه حتى إذا تم استخدام ذاكرة التخزين المؤقت لـ Redis ، فستكون هناك درجة معينة من الاستهلاك على نقل الشبكة. في التطبيقات الفعلية ، سيكون هناك بعض البيانات ذات التغييرات المنخفضة للغاية ، والتي يمكن تخزينها مؤقتًا داخل التطبيق. بالنسبة لبعض البيانات ذات المتطلبات الأقل في الوقت الفعلي ، يمكن أيضًا تخزينها مؤقتًا في الطلب لفترة معينة من الوقت لتقليل الوصول إلى redis وتحسين سرعة الاستجابة
نظرًا لأن Redis لديها بعض أوجه القصور في تنفيذ ذاكرة التخزين المؤقت لـ Spring في إطار عمل الربيع-قد تنشأ بعض المشكلات عند استخدامه ، لذلك لن نقوم بتمديده بناءً على التنفيذ الأصلي. سنشير مباشرة إلى طريقة التنفيذ لتنفيذ واجهات ذاكرة التخزين المؤقت و cachemanager
تجدر الإشارة أيضًا إلى أن التطبيقات التي تنشر عمومًا عقدًا متعددة ، وأن ذاكرة التخزين المؤقت من المستوى الأول هي ذاكرة التخزين المؤقت داخل التطبيق ، لذلك عند تحديث البيانات ومسحها ، يجب إخطار جميع العقد بتنظيف ذاكرة التخزين المؤقت. هناك العديد من الطرق لتحقيق هذا التأثير ، مثل: Zookeeper ، MQ ، وما إلى ذلك ، ولكن نظرًا لاستخدام Redis Cache ، فإن Redis يدعم وظائف الاشتراك/النشر ، لذلك لا تعتمد على مكونات أخرى. يستخدم مباشرة قناة Redis لإعلام العقد الأخرى لتنظيف عمليات ذاكرة التخزين المؤقت.
فيما يلي خطوات التغليف المبتدئين والرمز المصدري لـ Spring Boot + Spring Cache لتنفيذ ذاكرة التخزين المؤقت على مستوى (Redis + Caffeine)
تحديد خصائص تكوين فئة خاصية
package com.itopener.cache.redis.caffeine.spring.boot.autoconfigure ؛ استيراد java.util.hashmap ؛ استيراد java.util.hashset ؛ استيراد java.util.map ؛ Author fuwei.deng * date 29 يناير ، 2018 في 11:32:15 AM * VERVENT 1.0.0 */ @configurationProperties (prefix = "spring.cache.multi") الطبقة العامة cacherediscaffeineProperties {private set <string> cachenames = new mashset <> / ** ما إذا كنت لتخزين القيم الفارغة ، صواب الافتراضي ، منع اختراق ذاكرة التخزين المؤقت*/ private boolean cachenullvalues = true ؛ ؟ / ** بادئة مفتاح ذاكرة التخزين المؤقت*/ سلسلة خاصة cacheprefix ؛ redis private = new redis () ؛ الكافيين الكافيين الخاص = الكافيين الجديد () ؛ redis الفئة العامة { / ** وقت انتهاء الصلاحية العالمية ، الوحدة ميلي ثانية ، انتهاء الصلاحية الافتراضية* / private long defaultexpiration = 0 ؛ / ** وقت انتهاء الصلاحية ، الوحدة ميلي ثانية ، أولوية أعلى من DefaultExpiration*/ Private Map <String ، Long> Expires = New HashMap <> () ؛ / ** إخطار العقد الأخرى لأسماء الموضوعات عند تحديثات ذاكرة التخزين المؤقت*/ private string topic = "ذاكرة التخزين المؤقت: redis: caffeine: topic" ؛ Public GetDefaultExpiration () {return defaultexpiration ؛ } public void setDefaultExpiration (defaultExpiration) {this.defaultExpiration = defaultExpiration ؛ } الخريطة العامة <string ، long> getExpires () {return Expires ؛ } public void setExpires (Map <String ، Long> Expiers) {this.expires = Expires ؛ } السلسلة العامة getTopic () {return topic ؛ } public void settopic (string topic) {this.topic = topic ؛ }} الفئة العامة caffeine { / ** وقت انتهاء الصلاحية بعد الوصول ، بالمللي ثانية* / private long ExpireeaxTAccess ؛ / ** وقت انتهاء الصلاحية بعد الكتابة ، وحدة ميلي ثانية*/ private long Expireexterwrite ؛ / ** تحديث الوقت بعد الكتابة ، الوحدة ميلي ثانية*/ خاص طويل من RefreshAfterWrite ؛ / ** حجم التهيئة*/ private int initialcapacity ؛ / ** الحد الأقصى لعدد كائنات ذاكرة التخزين المؤقت ، فإن ذاكرة التخزين المؤقت الموضوعة قبل أن تتجاوز هذا الرقم سيكون غير صالح*/ الحد الأقصى الطويل الخاص ؛ /** نظرًا لأن الوزن يجب توفيره بواسطة كائنات ذاكرة التخزين المؤقت ، فإنه غير مناسب جدًا لسيناريوهات مثل استخدام ذاكرة التخزين المؤقت الربيع ، لذلك لا يتم دعم التكوين بعد*/// الحد الأقصى الطويل ؛ Public GetExpirafterAccess () {return ExpiReafterAccess ؛ } public void setExpirafterAccess (expireaxterAccess) {this.expirafterAccess = ExpiReafterAccess ؛ } public getExpiReletwrite () {return ExpiReafterWrite ؛ } public void setexpirwrite (expireafterwrite طويل) {this.expirafterWrite = ExpiReafterWrite ؛ } public getRefreshAfterWrite () {return RefreshAfterWrite ؛ } public void setRefreshAfterWrite (long refreshAfterWrite) {this.refreshafterWrite = shreffterWrite ؛ } public int getInitialCapacity () {return inialCapacity ؛ } public void setInitialCapacity (int initialCapacity) {this.initialCapacity = initialCapacity ؛ } public getMaximumSize () {return maximumsize ؛ } public void setMaximumSize (maximumsize long) {this.maximumsize = maximumSize ؛ }} مجموعة عامة <string> getCachenames () {return cachenames ؛ } public void setCachenames (set <String> cachenames) {this.cachenames = cachenames ؛ } المنطقية العامة iscachenullvalues () {return cachenullvalues ؛ } باطلة عامة setcachenullvalues (Loolean cachenullvalues) {this.cachenullvalues = cachenullvalues ؛ } boolean public Isdynamic () {return dynamic ؛ } public void setDynamic (Boolean Dynamic) {this.dynamic = Dynamic ؛ } السلسلة العامة getCachePrefix () {return cacheprefix ؛ } public void setCachePrefix (String cacheprefix) {this.cacheprefix = cacheprefix ؛ } redis getRedis () {return redis ؛ } public void setRedis (redis redis) {this.redis = redis ؛ } الكافيين العام getcaffeine () {return caffeine ؛ } public void setcaffeine (الكافيين الكافيين) {this.caffeine = caffeine ؛ }} هناك فئة مجردة AbstractValueadAptingCache التي تنفذ واجهة ذاكرة التخزين المؤقت في ذاكرة التخزين المؤقت لـ Spring ، والتي تحتوي على عبوة القيم الفارغة وتغليف قيم ذاكرة التخزين المؤقت ، لذلك ليست هناك حاجة لتنفيذ واجهة ذاكرة التخزين المؤقت ، وتنفيذ مباشرة فئة AbstractValueadApingCache Abstract.
package com.itopener.cache.redis.caffeine.spring.boot.autoconfigure.support ؛ import java.lang.reflect.constructor ؛ import java.util.map ؛ import java.util.set ؛ import java.util.concurrent.callable ؛ java.util.concurrent.locks.reentrantlock ؛ استيراد org.slf4j.logger ؛ استيراد org.slf4j.loggerfactory ؛ استيراد org.springframework.cache.support.abstractvalueadaptingcache org.springframework.util.StringUtils ؛ استيراد com.github.benmanes.caffeine.cache 5:24:11 PM * version 1.0.0 */public class rediscaffeinecache يمتد الملخصات ValueAdaptingCache {private final logger logger = loggerfactory.getLogger (rediscaffeinecache.class) ؛ اسم السلسلة الخاصة ؛ redistemplate الخاص <كائن ، كائن> redistemplate ؛ ذاكرة التخزين المؤقت الخاصة <Object ، Object> caffeinecache ؛ سلسلة خاصة cacheprefix ؛ الخاص الطويل الافتراضي = 0 ؛ خريطة خاصة <String ، Long> تنتهي ؛ موضوع السلسلة الخاصة = "ذاكرة التخزين المؤقت: redis: الكافيين: الموضوع" ؛ Rediscaffeinecache المحمي (Boolean Allownullvalues) {super (olowowlvalues) ؛ } rediscaffeinecache العامة (اسم السلسلة ، RediStemplate <object ، object> redistemplate ، ذاكرة التخزين المؤقت <object ، كائن> caffeinecache ، cacherediscaffeineProperties cacherediscaffeineProperties) this.name = name ؛ this.redistemplate = redistemplate ؛ this.caffeinecache = caffeinecache ؛ this.cacheprefix = cacherediscaffeineProperties.getCachePrefix () ؛ this.defaultExpiration = cacherediscaffeineProperties.getRedis (). getDefaultExpiration () ؛ this.expires = cacherediscaffeineProperties.getRedis (). getExpires () ؛ this.topic = cacherediscaffeineProperties.getRedis (). getTopic () ؛ } Override public string getName () {return this.name ؛ } Override public object getNativeCache () {return this ؛ } suppressWarnings ("Unchecked") Override public <t> t get (مفتاح الكائن ، القابل للاتصال <T> valueloader) {كائن value = lookup (مفتاح) ؛ if (value! = null) {return (t) value ؛ } reentrantlock lock = جديد reentrantlock () ؛ حاول {lock.lock () ؛ القيمة = lookup (مفتاح) ؛ if (value! = null) {return (t) value ؛ } value = valueloader.call () ؛ كائن storevalue = tostorevalue (valueloader.call ()) ؛ وضع (مفتاح ، متجر المتجر) ؛ عائد (ر) القيمة ؛ } catch (استثناء e) {try {class <؟> c = class.forname ("org.springframework.cache.cache $ valueretrivalexception") ؛ مُنشئ <؟> constructor = c.getConstructor (object.class ، callable.class ، throwable.class) ؛ RunTimeException استثناء = (RunTimeException) Constructor.newinstance (Key ، Valueloader ، E.GetCause ()) ؛ رمي الاستثناء } catch (استثناء e1) {رمي new alficalstateException (e1) ؛ }} أخيرًا {lock.unlock () ؛ }} Override public void pub (مفتاح الكائن ، قيمة الكائن) {if (! super.isallownullvalues () && value == null) {this.evict (key) ؛ يعود؛ } انتهاء صلاحية طويلة = getExpire () ؛ if (expire> 0) {redistemplate.opsforvalue (). } آخر {redistemplate.opsforvalue (). set (getKey (key) ، tostorevalue (value)) ؛ } push (cachemessage جديد (this.name ، key)) ؛ caffeinecache.put (المفتاح ، القيمة) ؛ } Override publuerrapper putifabsent (مفتاح الكائن ، قيمة الكائن) {Object cachekey = getKey (key) ؛ كائن prevvalue = فارغة ؛ // النظر في استخدام الأقفال الموزعة ، أو تغيير setifabsent redis إلى التشغيل الذري المتزامن (المفتاح) {prevValue = redistemplate.opsforvalue (). get (cachekey) ؛ if (prevValue == null) {long Expire = getExPire () ؛ if (expire> 0) {redistemplate.opsforvalue (). } آخر {redistemplate.opsforvalue (). set (getKey (key) ، tostorevalue (value)) ؛ } push (cachemessage جديد (this.name ، key)) ؛ caffeinecache.put (المفتاح ، tostorevalue (القيمة)) ؛ }} إرجاع tovaluewrapper (prevvalue) ؛ } Override public void Evict (مفتاح الكائن) {// أولاً ، قم بمسح البيانات المخزنة مؤقتًا في redis ، ثم قم بمسح ذاكرة التخزين المؤقت في الكافيين ، لتجنب ذاكرة التخزين المؤقت في فترة قصيرة من الزمن إذا تم مسح ذاكرة التخزين المؤقت أولاً ، وسيتم تحميل الطلبات الأخرى من إعادة التقييم إلى إعادة تأكيد Caffeine.delete (الحصول على مفتاح)) ؛ دفع (cachemessage جديد (this.name ، المفتاح)) ؛ caffeinecache.invalidate (مفتاح) ؛ } override public void clear () {// مسح البيانات المخزنة في إعادة التدوير ، ثم قم بمسح ذاكرة التخزين المؤقت في الكافيين ، لتجنب ذاكرة التخزين المؤقت في فترة زمنية قصيرة إذا تم مسح ذاكرة التخزين المؤقت أولاً ، ثم سيتم تحميل طلبات أخرى من إعادة التقييم إلى الكافيين من مجموعة <Bision> مفاتيح = redistmplate. لـ (مفتاح الكائن: مفاتيح) {redistemplate.delete (مفتاح) ؛ } push (cachemessage جديد (this.name ، null)) ؛ caffeinecache.invalidateall () ؛ } Override محمي الكائن Lookup (مفتاح الكائن) {Object cachekey = getKey (key) ؛ قيمة الكائن = caffeinecache.getifpresent (المفتاح) ؛ if (value! = null) {logger.debug ("الحصول على ذاكرة التخزين المؤقت من الكافيين ، المفتاح هو: {}" ، cachekey) ؛ قيمة الإرجاع } value = redistemplate.opsforvalue (). get (cachekey) ؛ if (value! = null) {logger.debug ("احصل على ذاكرة التخزين المؤقت من redis ووضعها في الكافيين ، المفتاح هو: {}" ، cachekey) ؛ cacheinecache.put (المفتاح ، القيمة) ؛ } قيمة الإرجاع ؛ } كائن خاص getKey (مفتاح الكائن) {return this.name.concat (":"). concat (stringUtils.isempty (cacheprefix)؟ key.toString (): cacheprefix.concat (":"). concat (key.toString ())) ؛ } private getExpire () {long Expire = defaultExpiration ؛ CachenameExpire Long = Expires.get (this.name) ؛ إرجاع cachenameexpire == null؟ Expire: cachenameexpire.longvalue () ؛ } / ** * description إخطار العقد الأخرى لتنظيف ذاكرة التخزين المؤقت المحلية عندما يتغير ذاكرة التخزين المؤقت * Author fuwei.deng * date 31 يناير 2018 في 3:20:28 PM * version 1.0.0 * param message * / private void push (cachemessage message) {Redistemplate.Convertandsend (topic ، message) ؛ } / ** * description قم بتنظيف ذاكرة التخزين المؤقت المحلية * Author fuwei.deng * date 31 يناير 2018 في 3:15:39 PM * version 1.0.0 * @param مفتاح * / public void clearlocal (مفتاح الكائن) if (key == null) {caffeinecache.invalidateall () ؛ } آخر {caffeinecache.invalidate (key) ؛ }}} تنفيذ واجهة CacheManager
package com.itopener.cache.redis.caffeine.spring.boot.autoconfigure.support ؛ import java.util.collection ؛ import java.util.set ؛ import java.util.concurrent.concurrenthashmap ؛ import java.util.util.concurrent.curentmap ؛ java.util.concurrent.timeunit ؛ استيراد org.slf4j.logger ؛ استيراد org.slf4j.loggerfactory ؛ استيراد org.springframework.cache com.github.benmanes.caffeine.cache.caffeine ؛ import com.itopener.cache.caffeine.spring.boot.autoconfigure.cacherediscaffeiSaffeInproperties ؛/** * author fuwei.deng * @Date 26 ، 2018 في 5:24 Rediscaffeinecachemanager ينفذ Cachemanager {private final logger logger = loggerFactory.getLogger (rediscaffeinecachemanager.class) ؛ ConcurrentMap الخاص <string ، cache> cachemap = concurrenthashMap الجديد <string ، cache> () ؛ CacherediscaffeineProperties الخاصة cacherediscaffeineProperties ؛ redistemplate الخاص <كائن ، كائن> redistemplate ؛ ديناميكية منطقية خاصة = صواب ؛ مجموعة خاصة <string> cachenames ؛ Rediscaffeinecaffeinemanager (CacherediscaffeineProperties CacherediscaffeineProperties ، redistemplate <object ، object> redistemplate) {super () ؛ this.cacherediscaffeineProperties = cacherediscaffeineProperties ؛ this.redistemplate = redistemplate ؛ this.dynamic = cacherediscaffeineProperties.isdynamic () ؛ this.cachenames = cacherediscaffeineProperties.getCachenames () ؛ } Override public cache getCache (اسم السلسلة) {cache cache = cachemap.get (name) ؛ if (cache! = null) {return cache ؛ } if (! Dynamic &&! cachenames.contains (name)) {return cache ؛ } ذاكرة التخزين المؤقت = rediscaffeinecache الجديد (الاسم ، redistemplate ، cache () ، cacherediscaffeineProperties) ؛ cache oldcache = cachemap.putifabsent (الاسم ، ذاكرة التخزين المؤقت) ؛ logger.debug ("إنشاء مثيل ذاكرة التخزين المؤقت ، اسم ذاكرة التخزين المؤقت هو: {}" ، الاسم) ؛ إرجاع OldCache == NULL؟ ذاكرة التخزين المؤقت: OldCache ؛ } public com.github.benmanes.caffeine.cache.cache <object ، object> caffeinecache () {caffeine <object ، object> cacheBuilder = caffeine.newbuilder () ؛ if (cacherediscaffeineProperties.getCaffeine (). } if (cacherediscaffeineProperties.getCaffeine (). } if (cacherediscaffeineProperties.getCaffeine (). } if (cacherediscaffeineProperties.getCaffeine (). getMaximumSize ()> 0) {cacheBuilder.MaximumSize (cacherediscaffeineProperties.getcaffeine (). getMaximumsize ()) ؛ } if (cacherediscaffeineProperties.getCaffeine (). getRefreshafterWrite ()> 0) {cacheBuilder.RefreshafterWrite (cacherediscaffeineProperties.getcaffeine (). } إرجاع cacheBuilder.build () ؛ } Override Public Collection <Tring> getCachenames () {return this.cachenames ؛ } void public clearlocal (سلسلة cachename ، مفتاح الكائن) {cache cache = cachemap.get (cachename) ؛ if (cache == null) {return ؛ } rediscaffeinecache rediscaffeinecache = (rediscaffeinecache) ذاكرة التخزين المؤقت ؛ rediscaffeinecache.clearlocal (مفتاح) ؛ }} إعادة نشر رسالة/اشتراك ، فئة الرسائل للإرسال
package com.itopener.cache.redis.caffeine.spring.boot.autoconfigure.support ؛ استيراد java.io.serializable ؛/** * @auuthor fuwei.deng * date inialize 29 ، 2018 1:31:17 pm * @ @ @ @ SerialVersionuid = 5987219310442078193L ؛ خيط خاص cachename ؛ مفتاح الكائن الخاص ؛ cachemessage العامة (سلسلة cachename ، مفتاح الكائن) {super () ؛ this.cachename = cachename ؛ this.key = المفتاح ؛ } السلسلة العامة getCachename () {return cachename ؛ } public void setCachename (String cachename) {this.cachename = cachename ؛ } الكائن العام getKey () {return Key ؛ } public void setKey (مفتاح الكائن) {this.key = key ؛ }} يتطلب الاستماع إلى رسائل redis واجهة messagelistener
package com.itopener.cache.redis.caffeine.spring.boot.autoconfigure.support ؛ استيراد org.slf4j.logger ؛ استيراد org.slf4j.loggerfactory org.springframework.data.redis.connection.messageListener ؛ استيراد org.springframework.data.redis.core.redistemplate ؛/** * @author fuwei.deng * @ @1 يناير 2018 في 5:22:33 pm * @vivers logger logger = loggerfactory.getLogger (cachemessagelistener.class) ؛ redistemplate الخاص <كائن ، كائن> redistemplate ؛ rediscaffeinecachemanager private rediscaffeinecachemanager ؛ cachemessagelistener العامة (Redistemplate <object ، object> redistemplate ، rediscaffeinecachemanager rediscaffeinecachemanager) {super () ؛ this.redistemplate = redistemplate ؛ this.rediscaffeinecachemanager = rediscaffeinecachemanager ؛ } Override public void onMessage (رسالة رسالة ، بايت [] نمط) {cachemessage cachemessage = (cachemessage) redistemplate.getValueserializer (). deserialize (message.getBody ()) ؛ logger.debug ("استلام رسالة موضوع redis ، مسح ذاكرة التخزين المؤقت المحلية ، cachename هو {} ، المفتاح هو {}" ، cachemessage.getCachename () ، cachemessage.getkey ()) ؛ rediscaffeinecachemanager.clearlocal (cacheMessage.getCachename () ، cachemessage.getkey ()) ؛ }} أضف فئة تكوين التمهيد الربيعي
package com.itopener.cache.redis.caffeine.spring.boot.autoconfigure ؛ استيراد org.springframework.beans.factory.annotation.autowired ؛ استيراد org.springframework.boot.autoconfigure.autoconfiguration ؛ org.springframework.boot.autoconfigure.condition.conditionalonbean ؛ استيراد org.springframework.boot.autoconfigure.data.redis.redisautoConfiguration org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.listener.ChannelTopic;import org.springframework.data.redis.listener.RedisMessageListenerContainer;import com.itopener.cache.redis.caffeine.spring.boot.autoconfigure.support.CacheMessageListener;import com.itopener.cache.redis.caffeine.spring.boot.autoconfigure.support.RedisCaffeineCacheManager;/** * @author fuwei.deng * @date January 26, 2018 at 5:23:03 pm * @version 1.0.0 */@Configuration@AutoConfigureAfter(RedisAutoConfiguration.class)@EnableConfigurationProperties(CacheRedisCaffeineProperties.class)public class CacheRedisCaffeineAutoConfiguration { @Autowired private CacheRedisCaffeineProperties cacheRedisCaffeineProperties; @Bean @ConditionalOnBean(RedisTemplate.class) public RedisCaffeineCaffeine cacheManager cacheManager(RedisTemplate<Object, Object> redisTemplate) { return new RedisCaffeineCacheManager(cacheRedisCaffeineProperties, redisTemplate); } @Bean public RedisMessageListenerContainer redisMessageListenerContainer(RedisTemplate<Object, Object> redisTemplate, RedisCaffeineCacheManager redisCaffeineCacheManager) { RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer(); redisMessageListenerContainer.setConnectionFactory(redisTemplate.getConnectionFactory()); CacheMessageListener cacheMessageListener = new CacheMessageListener(redisTemplate, redisCaffeineCaffeineCacheManager); redisMessageListenerContainer.addMessageListener(cacheMessageListener, new ChannelTopic(cacheRedisCaffeineProperties.getRedis().getTopic())); return redisMessageListenerContainer; }}在resources/META-INF/spring.factories文件中增加spring boot配置扫描
# Auto Configureorg.springframework.boot.autoconfigure.EnableAutoConfiguration=/com.itopener.cache.redis.caffeine.spring.boot.autoconfigure.CacheRedisCaffeineAutoConfiguration
接下来就可以使用maven引入使用了
<dependency> <groupId>com.itopener</groupId> <artifactId>cache-redis-caffeine-spring-boot-starter</artifactId> <version>1.0.0-SNAPSHOT</version> <type>pom</type></dependency>
在启动类上增加@EnableCaching注解,在需要缓存的方法上增加@Cacheable注解
package com.itopener.demo.cache.redis.caffeine.service;import java.util.Random;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.cache.annotation.CacheEvict;import org.springframework.cache.annotation.CachePut;import org.springframework.cache.annotation.CachePut;import org.springframework.cache.annotation.Cacheable;import org.springframework.stereotype.Service;import com.itopener.demo.cache.redis.caffeine.vo.UserVO;import com.itopener.utils.TimestampUtil;@Servicepublic class CacheRedisCaffeineService { private final Logger logger = LoggerFactory.getLogger(CacheRedisCaffeineService.class); @Cacheable(key = "'cache_user_id_' + #id", value = "userIdCache", cacheManager = "cacheManager") public UserVO get(long id) { logger.info("get by id from db"); UserVO user = new UserVO(); user.setId(id); user.setName("name" + id); user.setCreateTime(TimestampUtil.current()); return user; } @Cacheable(key = "'cache_user_name_' + #name", value = "userNameCache", cacheManager = "cacheManager") public UserVO get(String name) { logger.info("get by name from db"); UserVO user = new UserVO(); user.setId(new Random().nextLong()); user.setName (name) ؛ user.setCreateTime(TimestampUtil.current()); return user; } @CachePut(key = "'cache_user_id_' + #userVO.id", value = "userIdCache", cacheManager = "cacheManager") public UserVO update(UserVO userVO) { logger.info("update to db"); userVO.setCreateTime(TimestampUtil.current()); return userVO; } @CacheEvict(key = "'cache_user_id_' + #id", value = "userIdCache", cacheManager = "cacheManager") public void delete(long id) { logger.info("delete from db"); }} properties文件中redis的配置跟使用redis是一样的,可以增加两级缓存的配置
#两级缓存的配置spring.cache.multi.caffeine.expireAfterAccess=5000spring.cache.multi.redis.defaultExpiration=60000#spring cache配置spring.cache.cache-names=userIdCache,userNameCache#redis配置#spring.redis.timeout=10000#spring.redis.password=redispwd#redis pool#spring.redis.pool.maxIdle=10#spring.redis.pool.minIdle=2#spring.redis.pool.maxActive=10#spring.redis.pool.maxWait=3000#redis clusterspring.redis.cluster.nodes=127.0.0.1:7001,127.0.0.1:7002,127.0.0.1:7003,127.0.0.1:7004,127.0.0.1:7005,127.0.0.1:7006spring.redis.cluster.maxRedirects=3
ممتد
个人认为redisson的封装更方便一些
后续可以增加对于缓存命中率的统计endpoint,这样就可以更好的监控各个缓存的命中情况,以便对缓存配置进行优化
源码下载
starter目录:springboot / itopener-parent / spring-boot-starters-parent / cache-redis-caffeine-spring-boot-starter-parent
示例代码目录: springboot / itopener-parent / demo-parent / demo-cache-redis-caffeine
ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.