מבוא לחסה - לקוח Java Redis

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

מאמר זה הוא מבוא לחסה, לקוח ג'אווה של Redis.

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

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

2. למה חסה?

סקרנו את ג'דיס באחד הפוסטים הקודמים. מה עושה חסה שונה?

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

הוא גם משתמש ב- Netty לצורך תקשורת עם השרת. זה גורם לממשק API "כבד יותר", אך גם מתאים יותר לשיתוף חיבור עם יותר משרשור אחד.

3. התקנה

3.1. תלות

נתחיל בהכרזה על התלות היחידה שנזדקק לה ב- pom.xml:

 io. חסה-ליבת חסה 5.0.1. שחרור 

ניתן לבדוק את הגרסה האחרונה של הספרייה במאגר Github או ב- Maven Central.

3.2. התקנת רדיס

נצטרך להתקין ולהפעיל לפחות מופע אחד של Redis, שניים אם ברצוננו לבדוק מצב אשכולות או זקיף (למרות שמצב סנטינל דורש שלושה שרתים לתפקד כראוי.) למאמר זה, אנו משתמשים ב- 4.0.x - הגרסה האחרונה יציבה ברגע זה.

מידע נוסף על תחילת העבודה עם Redis ניתן למצוא כאן, כולל הורדות עבור Linux ו- MacOS.

Redis אינה תומכת רשמית ב- Windows, אך יש כאן יציאת שרת. אנו יכולים גם להפעיל את Redis ב- Docker המהווה אלטרנטיבה טובה יותר עבור Windows 10 ודרך מהירה להתקדם.

4. חיבורים

4.1. חיבור לשרת

החיבור ל- Redis מורכב מארבעה שלבים:

  1. יצירת URI של רדיס
  2. באמצעות ה- URI להתחברות ל- a RedisClient
  3. פתיחת חיבור רדיס
  4. יצירת סט של RedisCommands

בואו נראה את היישום:

RedisClient redisClient = RedisClient .create ("redis: // [מוגן באמצעות דוא"ל]: 6379 /"); חיבור StatefulRedisConnection = redisClient.connect ();

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

RedisClient משתמשת במשאבי מערכת משמעותיים, מכיוון שהיא מחזיקה במשאבי Netty לצורך תקשורת עם שרת Redis. יישומים הדורשים חיבורים מרובים צריכים להשתמש ביחיד RedisClient.

4.2. URIs של Redis

אנו יוצרים a RedisClient על ידי העברת URI לשיטת המפעל הסטטי.

חסה מנצלת תחביר מותאם אישית ל- URI של רדיס. זו הסכימה:

redis: // [[email protected]] מארח [: port] [/ database] [? [פסק זמן = פסק זמן [d | h | m | s | ms | us | ns]] [& _database = database_]] 

ישנן ארבע תוכניות URI:

  • redis - שרת Redis עצמאי
  • rediss - שרת Redis עצמאי באמצעות חיבור SSL
  • שקע רדיס - שרת Redis עצמאי באמצעות שקע תחום של יוניקס
  • רדיס-זקיף - שרת Redis Sentinel

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

בדוגמה לעיל אנו משתמשים ב- חוּט יִצוּג. לחסה יש גם RedisURI כיתה לבניית קשרים. הוא מציע את בּוֹנֶה תבנית:

RedisURI.Builder .redis ("localhost", 6379) .auth ("סיסמה") .database (1) .build (); 

ובונה:

RedisURI חדש ("localhost", 6379, 60, TimeUnit.SECONDS); 

4.3. פקודות סינכרוניות

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

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

לאחר שנוצר חיבור, אנו משתמשים בו ליצירת קבוצת פקודות:

RedisCommands syncCommands = connection.sync (); 

כעת יש לנו ממשק אינטואיטיבי לתקשורת עם Redis.

אנחנו יכולים להגדיר ולקבל ערכי מחרוזת:

syncCommands.set ("מפתח", "שלום, רדיס!"); ערך מחרוזת = syncommands.get ("מפתח"); 

אנחנו יכולים לעבוד עם hashes:

syncCommands.hset ("recordName", "FirstName", "John"); syncCommands.hset ("recordName", "LastName", "Smith"); Map record = syncCommands.hgetall ("recordName"); 

אנו נסקור את רדיס בהמשך המאמר.

ה- API הסינכרוני של חסה משתמש ב- API האסינכרוני. החסימה נעשית עבורנו ברמת הפיקוד. משמעות הדבר היא כי יותר מלקוח אחד יכול לשתף חיבור סינכרוני.

4.4. פקודות אסינכרוניות

בואו נסתכל על הפקודות הא-סינכרוניות:

RedisAsyncCommands asyncCommands = connection.async (); 

אנו מאחזרים קבוצה של RedisAsyncCommands מהחיבור, בדומה לאופן שבו השגנו את הסט הסינכרוני. פקודות אלה מחזירות א RedisFuture (שהוא א העתיד כְּלַפֵּי פְּנִים):

תוצאה של RedisFuture = asyncCommands.get ("מפתח"); 

מדריך לעבודה עם א העתיד ניתן למצוא כאן.

4.5. ממשק API תגובתי

לבסוף, נראה כיצד לעבוד עם ממשק API תגובתי שאינו חוסם:

RedisStringReactiveCommands reactiveCommands = connection.reactive (); 

פקודות אלה מחזירות תוצאות עטופות ב מונו או א שֶׁטֶף מ- Project Reactor.

מדריך לעבודה עם Project Reactor נמצא כאן.

5. מבני נתונים של רדיס

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

5.1. רשימות

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

asyncCommands.lpush ("משימות", "firstTask"); asyncCommands.lpush ("משימות", "secondTask"); RedisFuture redisFuture = asyncCommands.rpop ("משימות"); מחרוזת nextTask = redisFuture.get (); 

בדוגמה זו, משימה הבאה שווים "firstTask“. לפוש דוחף ערכים לראש הרשימה ואז rpop קופץ ערכים מסוף הרשימה.

אנחנו יכולים גם לפוצץ אלמנטים מהצד השני:

asyncCommands.del ("משימות"); asyncCommands.lpush ("משימות", "firstTask"); asyncCommands.lpush ("משימות", "secondTask"); redisFuture = asyncCommands.lpop ("משימות"); מחרוזת nextTask = redisFuture.get (); 

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

5.2. סטים

ערכות Redis הן אוספים ללא סדר מיתרים דומה ל- Java סטים; אין אלמנטים כפולים:

asyncCommands.sadd ("חיות מחמד", "כלב"); asyncCommands.sadd ("חיות מחמד", "חתול"); asyncCommands.sadd ("חיות מחמד", "חתול"); RedisFuture חיות מחמד = asyncCommands.smembers ("כינויים"); RedisFuture קיים = asyncCommands.sismember ("חיות מחמד", "כלב"); 

כאשר אנו מאחזרים את ערכת Redis כ- מַעֲרֶכֶתהגודל הוא שניים, שכן הכפילות "חתול" התעלם. כאשר אנו מבררים על Redis את קיומו של "כֶּלֶב" עם סיסמבר, התגובה היא נָכוֹן.

5.3. האש

בדקנו בקצרה דוגמה של hashes קודם לכן. הם שווים הסבר מהיר.

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

asyncCommands.hset ("שם רשומה", "שם פרטי", "ג'ון"); asyncCommands.hset ("recordName", "LastName", "Smith"); RedisFuture lastName = syncCommands.hget ("שם רשומה", "שם משפחה"); RedisFuture record = syncCommands.hgetall ("recordName"); 

אנו משתמשים hset להוסיף שדות לחשיש, להעביר את שם החשיש, שם השדה וערך.

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

5.4. סטים ממוינים

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

פריטים מתווספים בדרגה ומאוחזרים בטווח:

asyncCommands.zadd ("sortedset", 1, "one"); asyncCommands.zadd ("sortedset", 4, "zero"); asyncCommands.zadd ("sortedset", 2, "two"); RedisFuture valuesForward = asyncCommands.zrange (מפתח, 0, 3); RedisFuture valuesReverse = asyncCommands.zrevrange (מפתח, 0, 3); 

הטיעון השני ל זאד הוא דרגה. אנו מאחזרים טווח לפי דרגה עם zrange לסדר עולה ו לסדר מחדש לירידה.

הוספנו "אֶפֶס"בדרגה 4, כך שזה יופיע בסוף ערכים קדימה ובתחילת ערכים הפוך.

6. עסקאות

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

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

בואו נסתכל על דוגמה:

asyncCommands.multi (); RedisFuture result1 = asyncCommands.set ("key1", "value1"); RedisFuture result2 = asyncCommands.set ("key2", "value2"); RedisFuture result3 = asyncCommands.set ("key3", "value3"); RedisFuture execResult = asyncCommands.exec (); TransactionResult transactionResult = execResult.get (); מחרוזת firstResult = transactionResult.get (0); מחרוזת secondResult = transactionResult.get (0); מחרוזת thirdResult = transactionResult.get (0); 

הקריאה ל רַב מתחיל את העסקה. כאשר מתחילה עסקה, הפקודות הבאות לא יבוצעו עד ביצוע () נקרא.

במצב סינכרוני הפקודות חוזרות ריק. במצב אסינכרוני הפקודות חוזרות RedisFuture . ביצוע מחזירה א TransactionResult שמכיל רשימת תגובות.

מאז RedisFutures גם לקבל את התוצאות שלהם, לקוחות API אסינכרוניים מקבלים את תוצאת העסקה בשני מקומות.

7. אצווה

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

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

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

יישומים אסינכרוניים יכולים לעקוף התנהגות זו:

commands.setAutoFlushCommands (false); רשימה עתיד = ArrayList חדש (); עבור (int i = 0; i <iterations; i ++) {futures.add (commands.set ("key-" + i, "value-" + i);} commands.flushCommands (); תוצאה בוליאנית = LettuceFutures.awaitAll (5, TimeUnit.SECONDS, futures.toArray (RedisFuture חדש [0])); 

עם setAutoFlushCommands מוגדר כ שֶׁקֶר, הבקשה חייבת להתקשר flushCommands באופן ידני. בדוגמה זו עמדנו בתור למספר רב מַעֲרֶכֶת פקודה ואז שטף את הערוץ. AwaitAll מחכה לכל RedisFutures להשלים.

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

8. פרסם / עשה מנוי

Redis מציעה מערכת הודעות פשוטה לפרסום / מנוי. מנויים צורכים הודעות מערוצים באמצעות הירשם כמנוי פקודה. ההודעות אינן קבועות; הם מועברים למשתמשים רק כאשר הם מנויים לערוץ.

Redis משתמשת בפאב / מערכת המשנה לצורך התראות על מערך הנתונים של Redis, מה שמאפשר ללקוחות לקבל אירועים אודות הגדרת מפתחות, מחיקה, תפוגה וכו '.

לפרטים נוספים, עיין בתיעוד כאן.

8.1. מָנוּי

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

מחלקה ציבורית מאזין מיישם RedisPubSubListener {@ העברת הודעה בטלנית ציבורית (ערוץ מחרוזת, הודעת מחרוזת) {log.debug ("יש {} בערוץ {}", הודעה, ערוץ); הודעה = מחרוזת חדשה (s2); }} 

אנו משתמשים ב- RedisClient לחיבור פאב / ערוץ משנה ולהתקין את המאזין:

StatefulRedisPubSubConnection חיבור = client.connectPubSub (); connection.addListener (מאזין חדש ()) RedisPubSubAsyncCommands async = connection.async (); async.subscribe ("ערוץ"); 

עם התקנת מאזין, אנו מאחזרים קבוצה של RedisPubSubAsyncCommands ולהירשם לערוץ.

8.2. מוֹצִיא לָאוֹר

פרסום הוא רק עניין של חיבור ערוץ פאב / משנה ואחזור הפקודות:

StatefulRedisPubSubConnection חיבור = client.connectPubSub (); RedisPubSubAsyncCommands async = connection.async (); async.publish ("ערוץ", "שלום, רדיס!"); 

פרסום דורש ערוץ והודעה.

8.3. מנויים תגובתי

חסה מציעה גם ממשק תגובתי למנוי לפאב / הודעות משנה:

StatefulRedisPubSubConnection חיבור = לקוח .connectPubSub (); RedisPubSubAsyncCommands reactive = חיבור .reactive (); reactive.observeChannels (). הירשם כמנוי (הודעה -> {log.debug ("יש {} בערוץ {}", הודעה, ערוץ); הודעה = מחרוזת חדשה (s2);}); reactive.subscribe ("ערוץ"). subscribe (); 

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

9. זמינות גבוהה

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

9.1. מאסטר / עבד

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

חסה יכולה להתחבר למערכות Master / Slave, לשאול אותן לטופולוגיה, ואז לבחור עבדים לפעולות קריאה, מה שיכול לשפר את התפוקה:

RedisClient redisClient = RedisClient.create (); חיבור StatefulRedisMasterSlaveConnection = MasterSlave.connect (redisClient, חדש Utf8StringCodec (), RedisURI.create ("redis: // localhost")); connection.setReadFrom (ReadFrom.SLAVE); 

9.2. זָקִיף

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

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

לשם כך אנו בונים אחר RedisURI ולחבר את שלנו RedisClient עם זה:

RedisURI redisUri = RedisURI.Builder. Sentinel ("sentinelhost1", "clustername"). WithSentinel ("sentinelhost2"). Build (); לקוח RedisClient = RedisClient חדש (redisUri); חיבור RedisConnection = client.connect (); 

בנינו את ה- URI עם שם המארח (או הכתובת) של הזקיף הראשון ושם האשכול, ואחריו כתובת הזקיף השנייה. כשאנחנו מתחברים לסנטינל, חסה שואלת אותו לגבי הטופולוגיה ומחזירה עבורנו חיבור לשרת הראשי הנוכחי.

התיעוד המלא זמין כאן.

9.3. אשכולות

Redis Cluster משתמש בתצורה מבוזרת כדי לספק זמינות גבוהה ותפוקה גבוהה.

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

RedisURI redisUri = RedisURI.Builder.redis ("localhost") .withPassword ("אימות"). Build (); RedisClusterClient clusterClient = RedisClusterClient .create (rediUri); StatefulRedisClusterConnection חיבור = clusterClient.connect (); RedisAdvancedClusterCommands syncCommands = חיבור. Sync (); 

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

מפרט מלא זמין כאן.

10. מסקנה

במדריך זה בדקנו כיצד להשתמש בחסה כדי לחבר ולשאול שרת Redis מתוך היישום שלנו.

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

דוגמאות קוד, כמו תמיד, ניתן למצוא באתר GitHub.


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