מדריך ל- Infinispan בג'אווה

1. סקירה כללית

במדריך זה נלמד על אינפיניספאן, חנות נתוני מפתח בזיכרון / ערך אשר נשלחת עם קבוצה חזקה יותר של תכונות מאשר כלים אחרים מאותה נישה.

כדי להבין כיצד זה עובד, נבנה פרויקט פשוט המציג את התכונות הנפוצות ביותר ונבדוק כיצד ניתן להשתמש בהם.

2. הגדרת פרויקט

כדי שנוכל להשתמש בזה בצורה זו, נצטרך להוסיף את התלות שלנו pom.xml.

ניתן למצוא את הגרסה האחרונה במאגר Maven Central:

 org.infinispan infinispan-core 9.1.5.גמר 

כל התשתית הבסיסית הנדרשת תטופל מעת לעת באופן פרוגרמטי.

3. CacheManager להכין

ה CacheManager הוא הבסיס לרוב התכונות בהן נשתמש. הוא משמש כמכולה לכל המטמונים המוצהרים, השולט על מחזור חייהם, ואחראי על התצורה הגלובלית.

אינפיניספאן ספינות עם דרך ממש קלה לבנות את CacheManager:

ציבורי DefaultCacheManager cacheManager () {להחזיר DefaultCacheManager חדש (); }

עכשיו אנחנו יכולים לבנות את המטמונים שלנו איתו.

4. הגדרת מטמון

מטמון מוגדר על ידי שם ותצורה. ניתן לבנות את התצורה הדרושה באמצעות המחלקה ConfigurationBuilder, כבר זמין בנתיב הכיתות שלנו.

כדי לבדוק את המטמונים שלנו, נבנה שיטה פשוטה המדמה שאילתות כבדות:

מחלקה ציבורית HelloWorldRepository {public String getHelloWorld () {try {System.out.println ("ביצוע שאילתה כבדה"); Thread.sleep (1000); } לתפוס (InterruptedException e) {// ... e.printStackTrace (); } להחזיר "שלום עולם!"; }}

כמו כן, כדי להיות מסוגל לבדוק אם קיימים שינויים במטמונים שלנו, Infinispan מספק הערה פשוטה @מַאֲזִין.

כאשר אנו מגדירים את המטמון שלנו, אנו יכולים להעביר אובייקט כלשהו המעוניין בכל אירוע שיקרה בתוכו, ו- Infinispan יודיע על כך בעת הטיפול במטמון:

@Listener class class CacheListener {@CacheEntryCreated public void entryCreated (CacheEntryCreatedEvent event) {this.printLog ("הוספת מפתח '" + event.getKey () + "' למטמון", אירוע); } @CacheEntryExpired כניסה בטלנית ציבורית Exppired (אירוע CacheEntryExpiredEvent) {this.printLog ("מפתח פג '" + event.getKey () + "' מהמטמון", אירוע); } @CacheEntryVisited entry void publicVisited (אירוע CacheEntryVisitedEvent) {this.printLog ("מפתח '" + event.getKey () + "' ביקר", אירוע); } @CacheEntryActivated ערך ריק חלל ציבורי Activated (CacheEntryActivatedEvent event) {this.printLog ("מפתח הפעלה '" + event.getKey () + "' במטמון", אירוע); } @CacheEntryPassivated כניסה בטלנית ציבורית Passivated (אירוע CacheEntryPassivatedEvent) {this.printLog ("מפתח מפעיל" "+ event.getKey () +" 'מהמטמון, אירוע); } @CacheEntryLoaded entry public voidLoaded (אירוע CacheEntryLoadedEvent) {this.printLog ("מפתח טעינה '" + event.getKey () + "' למטמון", אירוע); } @CacheEntriesEvicted ערכי חלל פומבייםEvicted (אירוע CacheEntriesEvictedEvent) {בונה StringBuilder = StringBuilder חדש (); event.getEntries (). forEach ((מפתח, ערך) -> builder.append (מפתח) .append (",")); System.out.println ("פינוי הערכים הבאים מהמטמון:" + builder.toString ()); } printLog ריק ריק (יומן מחרוזות, אירוע CacheEntryEvent) {if (! event.isPre ()) {System.out.println (log); }}}

לפני הדפסת ההודעה שלנו אנו בודקים אם האירוע שקיבל הודעה כבר קרה, מכיוון שבחלק מסוגי האירוע אינפיניספאן שולחת שתי התראות: אחת לפני ואחת מיד לאחר עיבודה.

עכשיו בואו לבנות שיטה לטיפול ביצירת המטמון בשבילנו:

מטמון buildCache פרטי (מחרוזת cacheName, DefaultCacheManager cacheManager, CacheListener מאזין, תצורת תצורה) {cacheManager.defineConfiguration (cacheName, תצורה); מטמון מטמון = cacheManager.getCache (cacheName); cache.addListener (מאזין); החזר מטמון; }

שימו לב כיצד אנו מעבירים תצורה אל CacheManagerולאחר מכן השתמש בו cacheName כדי להשיג את האובייקט המתאים למטמון המבוקש. שימו לב גם כיצד אנו מודיעים למאזין על אובייקט המטמון עצמו.

כעת נבדוק חמש תצורות מטמון שונות ונראה כיצד נוכל להגדיר אותן ולנצל אותן בצורה הטובה ביותר.

4.1. מטמון פשוט

ניתן להגדיר את סוג המטמון הפשוט ביותר בשורה אחת, בשיטה שלנו buildCache:

מטמון ציבורי simpleHelloWorldCache (DefaultCacheManager cacheManager, CacheListener מאזין) {להחזיר this.buildCache (SIMPLE_HELLO_WORLD_CACHE, cacheManager, מאזין, ConfigurationBuilder חדש (). build ()); }

כעת נוכל לבנות א שֵׁרוּת:

מחרוזת ציבורית findSimpleHelloWorld () {String cacheKey = "פשוט-שלום"; להחזיר simpleHelloWorldCache .computeIfAbsent (cacheKey, k -> repository.getHelloWorld ()); }

שימו לב כיצד אנו משתמשים במטמון, ראשית בדקו אם הערך המבוקש כבר נשמר במטמון. אם לא, נצטרך להתקשר לשלנו מאגר ואז שמור אותו במטמון.

בואו להוסיף שיטה פשוטה במבחנים שלנו כדי לתזמן את השיטות שלנו:

זמן מוגן זה (ספק הספק) {long millis = System.currentTimeMillis (); provider.get (); להחזיר System.currentTimeMillis () - מילי; }

בבדיקה נוכל לבדוק את הזמן שבין ביצוע שתי שיחות שיטה:

@ מבחן ציבורי בטל כאשר GetIsCalledTwoTimes_thenTheSecondShouldHitTheCache () {assertThat (timeThis (() -> helloWorldService.findSimpleHelloWorld ())) .isGreaterThanOrEqualTo (1000); assertThat (timeThis (() -> helloWorldService.findSimpleHelloWorld ())) .isLessThan (100); }

4.2. מטמון תפוגה

אנו יכולים להגדיר מטמון בו לכל הערכים אורך חיים, במילים אחרות, אלמנטים יוסרו מהמטמון לאחר תקופה נתונה. התצורה פשוטה למדי:

תצורה פרטית expiringConfiguration () {להחזיר ConfigurationBuilder חדש (). תפוגה () .lifespan (1, TimeUnit.SECONDS) .build (); }

כעת אנו בונים את המטמון באמצעות התצורה לעיל:

מטמון ציבורי שפג תוקף HelloWorldCache (DefaultCacheManager cacheManager, CacheListener listener) {החזר את this.buildCache (EXPIRING_HELLO_WORLD_CACHE, cacheManager, מאזין, expiringConfiguration ()); }

ולבסוף, השתמש בו בשיטה דומה מהמטמון הפשוט שלנו לעיל:

מחרוזת ציבורית findSimpleHelloWorldInExpiringCache () {String cacheKey = "simple-hello"; מחרוזת helloWorld = expiringHelloWorldCache.get (cacheKey); אם (helloWorld == null) {helloWorld = repository.getHelloWorld (); expiringHelloWorldCache.put (cacheKey, helloWorld); } תחזיר שלום עולם; }

בואו נבדוק את זמנינו שוב:

@ מבחן ציבורי בטל כאשר GetIsCalledTwoTimesQuickly_thenTheSecondShouldHitTheCache () {assertThat (timeThis (() -> helloWorldService.findExpiringHelloWorld ())) .isGreaterThanOrEqualTo (1000); assertThat (timeThis (() -> helloWorldService.findExpiringHelloWorld ())) .isLessThan (100); }

בהפעלתו, אנו רואים כי ברצף מהיר המטמון פוגע. להציג כי התפוגה יחסית לכניסתה לָשִׂים זמן, בואו נכריח את זה בכניסה שלנו:

@ מבחן ציבורי בטל כאשר GetIsCalledTwiceSparsely_thenNeitherHitsTheCache () זורק InterruptedException {assertThat (timeThis (() -> helloWorldService.findExpiringHelloWorld ())) .isGreaterThanOrEqualTo (1000); Thread.sleep (1100); assertThat (timeThis (() -> helloWorldService.findExpiringHelloWorld ())) .isGreaterThanOrEqualTo (1000); }

לאחר הפעלת הבדיקה, שים לב כיצד לאחר הזמן הנתון פג תוקף כניסתנו מהמטמון. אנו יכולים לאשר זאת על ידי עיון בשורות היומן המודפסות מהמאזין שלנו:

ביצוע שאילתה כבדה הוספת מפתח 'פשוט-שלום' למטמון פג המפתח 'פשוט-שלום' מהמטמון ביצוע כל שאילתה כבדה הוספת מפתח 'פשוט-שלום' למטמון

שים לב שהערך פג כשאנחנו מנסים לגשת אליו. Infinispan בודק אם ערך פג תוקף בשני רגעים: כאשר אנו מנסים לגשת אליו או כאשר חוט הקוצר סורק את המטמון.

אנו יכולים להשתמש בתפוגה גם במטמונים מבלי שזה בתצורה העיקרית שלהם. השיטה לָשִׂים מקבל טיעונים נוספים:

simpleHelloWorldCache.put (cacheKey, helloWorld, 10, TimeUnit.SECONDS);

לחלופין, במקום תוחלת חיים קבועה, אנו יכולים לתת לכניסה שלנו מקסימום זמן מת:

simpleHelloWorldCache.put (cacheKey, helloWorld, -1, TimeUnit.SECONDS, 10, TimeUnit.SECONDS);

באמצעות -1 לתכונת תוחלת החיים, המטמון לא יסבול מתפוגתו ממנו, אך כאשר אנו משלבים אותו עם 10 שניות של זמן מתאנו אומרים לאינפיניספן לפוג תוקף ערך זה אלא אם כן מבקרים אותו בפרק הזמן הזה.

4.3. פינוי מטמון

ב- Infinispan אנו יכולים להגביל את מספר הערכים במטמון נתון באמצעות ה- תצורת פינוי:

תצורה פרטית evictingConfiguration () {להחזיר ConfigurationBuilder חדש () .memory (). evictionType (EvictionType.COUNT). גודל (1) .build (); }

בדוגמה זו אנו מגבילים את הערכים המרביים במטמון זה לאחד, כלומר, אם ננסה להזין ערך אחר, הוא יפונה מהמטמון שלנו.

שוב, השיטה דומה לזו שכבר הוצגה כאן:

מחרוזת ציבורית findEvictingHelloWorld (מפתח מחרוזת) {ערך מחרוזת = evictingHelloWorldCache.get (מפתח); אם (value == null) {value = repository.getHelloWorld (); evictingHelloWorldCache.put (מפתח, ערך); } ערך החזרה; }

בואו נבנה את המבחן שלנו:

@Test הציבור בטל כאשרTwoAreAdded_thenFirstShouldntBeAvailable () {assertThat (timeThis (() -> helloWorldService.findEvictingHelloWorld ("מפתח 1"))) .isGreaterThanOrEqualTo (1000); assertThat (timeThis (() -> helloWorldService.findEvictingHelloWorld ("מפתח 2"))) .isGreaterThanOrEqualTo (1000); assertThat (timeThis (() -> helloWorldService.findEvictingHelloWorld ("מפתח 1"))) .isGreaterThanOrEqualTo (1000); }

בהפעלת המבחן נוכל להסתכל ביומן הפעילויות של המאזינים שלנו:

ביצוע שאילתה כבדה הוספת מפתח 'מפתח 1' למטמון ביצוע שאילתה כבדה פינוי הערכים הבאים מהמטמון: מפתח 1, הוספת מפתח 'מפתח 2' למטמון ביצוע שאילתה כבדה פינוי הערכים הבאים מהמטמון: מפתח 2, מפתח מפתח ' 1 'למטמון

בדוק כיצד המפתח הראשון הוסר אוטומטית מהמטמון כאשר הכנסנו את השני, ואז, השני הוסר גם הוא כדי לתת שוב מקום למפתח הראשון שלנו.

4.4. מטמון פסיבציה

ה מטמון פסיבציה הוא אחד המאפיינים החזקים של אינפיניספאן. על ידי שילוב של פסיבציה ופינוי, אנו יכולים ליצור מטמון שלא תופס הרבה זיכרון מבלי לאבד מידע.

בואו נסתכל על תצורת פסיבציה:

תצורה פרטית passivatingConfiguration () {החזר ConfigurationBuilder חדש () .memory (). evictionType (EvictionType.COUNT). גודל (1) .persistence (). passivation (true) // הפעלת פסיבציה .addSingleFileStore () // בקובץ יחיד .purgeOnStartup (true) // נקה את הקובץ בהפעלה .location (System.getProperty ("java.io.tmpdir")) .build (); }

אנו שוב מכריחים ערך אחד בלבד בזיכרון המטמון שלנו, אך אומרים לאינפיניספאן להעביר את הערכים הנותרים לפסיב, במקום פשוט להסיר אותם.

בואו נראה מה קורה כשאנחנו מנסים למלא יותר מערך אחד:

מחרוזת findPassivatingHelloWorld (מפתח מחרוזת) ציבורי {return passivatingHelloWorldCache.computeIfAbsent (key, k -> repository.getHelloWorld ()); }

בואו לבנות את המבחן שלנו ולהריץ אותו:

@Test הציבור בטל כאשרTwoAreAdded_thenTheFirstShouldBeAvailable () {assertThat (timeThis (() -> helloWorldService.findPassivatingHelloWorld ("מפתח 1"))) .isGreaterThanOrEqualTo (1000); assertThat (timeThis (() -> helloWorldService.findPassivatingHelloWorld ("מפתח 2"))) .isGreaterThanOrEqualTo (1000); assertThat (timeThis (() -> helloWorldService.findPassivatingHelloWorld ("מפתח 1"))) .isLessThan (100); }

עכשיו בואו נסתכל על פעילויות המאזינים שלנו:

ביצוע שאילתה כבדה הוספת מפתח 'מפתח 1' למטמון ביצוע שאילתה כבדה מפתח הפעלת 'מפתח 1' מהמטמון פינוי הערכים הבאים מהמטמון: מפתח 1, הוספת מפתח 'מפתח 2' למטמון הפעלת מפתח 'מפתח 2' מהמטמון פינוי הערכים הבאים מהמטמון: מפתח 2, טעינת מפתח 'מפתח 1' למטמון הפעלת מפתח 'מפתח 1' במטמון מפתח 'מפתח 1' ביקר

שימו לב כמה צעדים נדרשו כדי לשמור על המטמון שלנו עם ערך אחד בלבד. כמו כן, שימו לב לסדר הצעדים - פסיבציה, פינוי ואז טעינה ולאחריה הפעלה. בואו נראה מה המשמעות של השלבים האלה:

  • פסיביות - הכניסה שלנו מאוחסנת במקום אחר, הרחק מהאחסון הראשי של אינפיניספאן (במקרה זה, הזיכרון)
  • פינוי - הערך הוסר, כדי לפנות זיכרון ולשמור על המספר המרבי שהוגדר של רשומות במטמון
  • טוען - כשמנסים להגיע לערך הפסיבי שלנו, אינפיניספאן בודקת את תוכנו המאוחסן וטוענת את הערך שוב בזיכרון
  • הפעלה - הערך נגיש כעת ב- Infinispan

4.5. מטמון עסקה

אינפיניספאן נשלחת עם בקרת עסקאות חזקה. כמו המקביל למסד הנתונים, הוא שימושי בשמירה על תקינות כאשר יותר מחוט אחד מנסה לכתוב את אותו ערך.

בואו נראה כיצד נוכל להגדיר מטמון עם יכולות עסקאות:

תצורה פרטית transaktionalConfiguration () {החזר ConfigurationBuilder חדש () .transaction (). transactionMode (TransactionMode.TRANSACTIONAL) .lockingMode (LockingMode.PESSIMISTIC) .build (); }

כדי לאפשר בדיקה, בואו נבנה שתי שיטות - אחת המסיימת את העסקה במהירות, ואחת שנמשכת זמן מה:

מספר שלם ציבורי getQuickHowManyVisits () {TransactionManager tm = transactionalCache .getAdvancedCache (). getTransactionManager (); tm.begin (); מספר שלם howManyVisits = transactionalCache.get (KEY); howManyVisits ++; System.out.println ("אנסה להגדיר HowManyVisits ל-" + howManyVisits); שעון סטופר = שעון עצר חדש (); watch.start (); transactionalCache.put (KEY, howManyVisits); watch.stop (); System.out.println ("הצלחתי להגדיר את HowManyVisits ל-" + howManyVisits + "לאחר המתנה" + watch.getTotalTimeSeconds () + "שניות"); tm.commit (); להחזיר howManyVisits; }
בטל ציבורי startBackgroundBatch () {TransactionManager tm = transactionalCache .getAdvancedCache (). getTransactionManager (); tm.begin (); transactionalCache.put (KEY, 1000); System.out.println ("HowManyVisits אמור להיות כעת 1000," + "אך אנו מחזיקים את העסקה"); Thread.sleep (1000L); tm.rollback (); System.out.println ("האצווה האטית סבלה מההחזרה"); }

עכשיו בואו ניצור בדיקה שמבצעת את שתי השיטות ונבדוק כיצד תנהג אינפיניספן:

@Test הציבור בטל כאשרLockingAnEntry_thenItShouldBeInaccessible () זורק InterruptedException {Runnable backGroundJob = () -> transactionalService.startBackgroundBatch (); Thread backgroundThread = Thread חדש (backGroundJob); transactionalService.getQuickHowManyVisits (); backgroundThread.start (); Thread.sleep (100); // מאפשר לחכות לשרשור שלנו להתחמם assertThat (timeThis (() -> transactionalService.getQuickHowManyVisits ())) .isGreaterThan (500) .isLessThan (1000); }

אם נבצע אותו, נראה שוב את הפעילויות הבאות במסוף שלנו:

הוספת מפתח 'מפתח' למטמון מפתח 'מפתח' ביקר בחולה אנסה להגדיר את HowManyVisits ל- 1 הצלחתי להגדיר את HowManyVisits ל- 1 לאחר המתנה של 0.001 שניות HowManyVisits אמור להיות כעת 1000, אך אנו מחזיקים בעסק של מפתח 'מפתח' ביקר אני אנסה להגדיר את HowManyVisits ל- 2 הצלחתי להגדיר את HowManyVisits ל- 2 לאחר המתנה של 0.902 שניות.

בדוק את השעה בשרשור הראשי, מחכה לסיום העסקה שנוצרה בשיטה האיטית.

5. מסקנה

במאמר זה ראינו מה זה Infinispan, והוא כולל תכונות ויכולות מובילות כמטמון בתוך יישום.

כמו תמיד, ניתן למצוא את הקוד ב- Github.


$config[zx-auto] not found$config[zx-overlay] not found