javascript
Spring数据和Redis
本文是我們學(xué)院課程的一部分,標(biāo)題為Redis NoSQL鍵值存儲(chǔ) 。
這是Redis的速成班。 您將學(xué)習(xí)如何安裝Redis并啟動(dòng)服務(wù)器。 此外,您將在Redis命令行中亂七八糟。 接下來(lái)是更高級(jí)的主題,例如復(fù)制,分片和集群,同時(shí)還介紹了Redis與Spring Data的集成。 在這里查看 !
目錄
1.簡(jiǎn)介 2.先決條件 3.選擇Redis Java客戶端 4. Java應(yīng)用程序依賴性和Eclipse 5.在Spring數(shù)據(jù)Redis上配置獨(dú)立Redis 6.配置與分片(分區(qū))Redis的連接 7.配置與Redis群集的連接 8.使用Spring Data Redis訪問(wèn)Redis 9.使用Spring Data Redis進(jìn)行事務(wù) 10.使用Spring Data Redis進(jìn)行流水線 11.使用Spring Data Redis發(fā)布/訂閱 12.結(jié)論1.簡(jiǎn)介
創(chuàng)建Redis是為了解決實(shí)際軟件系統(tǒng)的實(shí)際問(wèn)題。 到目前為止,我們已經(jīng)探索了非常豐富的Redis功能集,但實(shí)際上并沒有在實(shí)際的應(yīng)用程序中使用它們。 為了填補(bǔ)這一空白,本教程的最后一部分專門介紹了此主題。 我們將構(gòu)建一個(gè)使用Redis和出色的Spring Data Redis ( http://projects.spring.io/spring-data-redis/ )項(xiàng)目以及Spring Framework ( http://projects.spring.io )的簡(jiǎn)單Java應(yīng)用程序。 / spring-framework / ),請(qǐng)參閱Spring項(xiàng)目組合( http://spring.io/ )。 撰寫本文時(shí), Spring Data Redis和Spring Framework的最新發(fā)布版本分別為1.2.0和4.0.2 。
在深入探討細(xì)節(jié)之前,值得一提的是,Redis支持各種應(yīng)用程序框架和編程語(yǔ)言。 客戶端的完整列表可在此處找到: http : //redis.io/clients 。
本教程的其余部分假定讀者能夠使用Java進(jìn)行編程,并且對(duì)Spring Framework ( http://projects.spring.io/spring-framework/ )具有基本的了解。
2.先決條件
Java開發(fā)人員的常用工具集包括JDK(Java開發(fā)工具包)和類似Eclipse或Intellij IDEA的IDE,以促進(jìn)應(yīng)用程序開發(fā)過(guò)程。 我們將使用的最新JDK版本是1.7_51 ,可以從http://www.oracle.com/technetwork/java/javase/downloads/index.html下載。
IDE的選擇是Eclipse ,它的最新版本是4.3.2 ,可以從https://www.eclipse.org/downloads/下載( 適用于Java開發(fā)人員的 Eclipse IDE,適用于Java EE開發(fā)人員的Eclipse IDE或Spring Tool Suite版本很好)。
3.選擇Redis Java客戶端
有幾種Java客戶端可用于從Java應(yīng)用程序訪問(wèn)Redis( Spring Data Redis也支持下面列出的所有客戶端):
- 杰迪斯: https : //github.com/xetorthio/jedis
- JRedis: https : //github.com/alphazero/jredis
- 生菜: https : //github.com/wg/lettuce
從功能的角度來(lái)看,它們都很相似,但是Jedis已獲得越來(lái)越多的普及并被廣泛使用。 話雖如此, Jedis也是我們應(yīng)用程序的選擇。
4. Java應(yīng)用程序依賴性和Eclipse
我們將要構(gòu)建的項(xiàng)目將介紹到目前為止已經(jīng)討論過(guò)的所有重要Redis功能,但這些功能來(lái)自應(yīng)用程序開發(fā)人員。 我們將從一個(gè)基本示例開始,假設(shè)有一個(gè)獨(dú)立的Redis實(shí)例并在某個(gè)地方運(yùn)行(讓我們將其稱為redis-host )。 大多數(shù)方案將以一個(gè)小的JUnit ( http://junit.org/ )測(cè)試片段的形式呈現(xiàn)。
Java世界中事實(shí)上的構(gòu)建和依賴性管理工具仍然是Apache Maven ( http://maven.apache.org/ ),我們將使用的版本是3.1.1,可以從http://maven.apache下載。 org / docs / 3.1.1 / release-notes.html 。 實(shí)際上,我們不會(huì)使用太多的Apache Maven來(lái)讓Eclipse代表我們完成這項(xiàng)工作,但是我們將研究基本的依賴項(xiàng)描述文件,該文件通常稱為pom.xml 。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.javacodegeeks</groupId><artifactId>redis</artifactId><version>0.0.1-SNAPSHOT</version><packaging>jar</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><spring.version>4.0.2.RELEASE</spring.version></properties><dependencies><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-redis</artifactId><version>1.2.0.RELEASE</version></dependency><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.4.1</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring.version}</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.0.13</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>${spring.version}</version><scope>test</scope></dependency><dependency><groupId>org.hamcrest</groupId><artifactId>hamcrest-all</artifactId><version>1.3</version><scope>test</scope></dependency><dependency><groupId>com.jayway.awaitility</groupId><artifactId>awaitility</artifactId><version>1.5.0</version><scope>test</scope></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.7</source><target>1.7</target></configuration></plugin></plugins></build> </project>如果您是經(jīng)驗(yàn)豐富的Java開發(fā)人員,那么pom.xml文件應(yīng)該非常熟悉。 但是對(duì)于新手來(lái)說(shuō),一些評(píng)論可能會(huì)有所幫助。 基本上,我們聲明名為com.javacodegeeks.redis的項(xiàng)目取決于:
- 杰迪斯( redis.clients.jedis )
- Spring Data Redis( org.springframework.data.spring-data-redis )
- Spring框架( org.springframework.spring-core , org.springframework.spring-context , org.springframework.spring-tx , org.springframework.spring-test )
- JUnit和隨附的測(cè)試腳手架( junit.junit , org.hamcrest.hamcrest-all , com.jayway.awaitility.awaitility )
至此,我們可以使用Existing Maven Project功能的Import(菜單文件-> Import…)將pom.xml導(dǎo)入Eclipse 。
圖1:將現(xiàn)有的Maven項(xiàng)目導(dǎo)入Eclipse
導(dǎo)入完成后,名為com.javacodegeeks.redis的項(xiàng)目(或只是redis ,具體取決于您的導(dǎo)入設(shè)置)應(yīng)出現(xiàn)在Eclipse Project Explorer視圖中。
圖2. Eclipse Project視圖中的com.javacodegeeks.redis
5.在Spring數(shù)據(jù)Redis上配置獨(dú)立Redis
Jedis的Redis配置從定義JedisConnectionFactory開始。 默認(rèn)情況下,Jedis使用連接池( http://en.wikipedia.org/wiki/Connection_pool ),以便每次都不創(chuàng)建到Redis服務(wù)器的連接,而是從可用連接池中借用它們。 總的來(lái)說(shuō),這被認(rèn)為是一種好的做法,因?yàn)閯?chuàng)建網(wǎng)絡(luò)連接的過(guò)程是一個(gè)相對(duì)昂貴的操作。
讓我們將連接池和連接工廠定義為單獨(dú)的Spring配置bean,以便可以由不同的應(yīng)用程序配置獨(dú)立地導(dǎo)入它。
package com.javacodegeeks.redis;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;import redis.clients.jedis.JedisPoolConfig; import redis.clients.jedis.Protocol;@Configuration public class ConnectionConfiguration {@Beanpublic JedisPoolConfig poolConfig() {final JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setTestOnBorrow( true );jedisPoolConfig.setMaxTotal( 10 );return jedisPoolConfig;}@Beanpublic JedisConnectionFactory connectionFactory() {final JedisConnectionFactory connectionFactory = new JedisConnectionFactory( poolConfig() ); connectionFactory.setHostName( "redis-host" );connectionFactory.setDatabase( Protocol.DEFAULT_DATABASE );connectionFactory.setPort( Protocol.DEFAULT_PORT ); return connectionFactory;} }在此代碼段中,我們將連接工廠配置為在具有最多10個(gè)連接池的redis-host上運(yùn)行的Redis實(shí)例。 test on borrow設(shè)置實(shí)際上確保從池借用的連接仍然有效并且可以使用(否則將重新創(chuàng)建連接)。
6.配置與分片(分區(qū))Redis的連接
在第4部分 Redis Sharding中 ,我們討論了客戶端分區(qū)。 實(shí)際上,Jedis提供了此功能,但不幸的是Spring Data Redis尚不支持此功能。
7.配置與Redis群集的連接
在第5部分“ Redis群集”中 ,我們發(fā)現(xiàn)了Redis的群集功能,并提到客戶端應(yīng)該支持和識(shí)別Redis協(xié)議中的更改,以便向正確的節(jié)點(diǎn)發(fā)出命令。 Jedis已經(jīng)提供了對(duì)Redis群集的支持,但是不幸的是Spring Data Redis尚不支持此功能。
8.使用Spring Data Redis訪問(wèn)Redis
Spring Data Redis在不同的Redis客戶端上提供一致而簡(jiǎn)潔的編程抽象(請(qǐng)參閱選擇Redis Java客戶端)。 這種抽象的核心是模板的概念:最簡(jiǎn)單的方法提供對(duì)所需功能的訪問(wèn),而無(wú)需花費(fèi)大量時(shí)間編寫樣板代碼。 如果是Redis,則為RedisTemplate 。
從本教程的前面的部分中,我們知道Redis支持多種原始數(shù)據(jù)類型:字符串和數(shù)字。 但是Java類型系統(tǒng)比這要豐富得多,這就是為什么RedisTemplate要求鍵的類型和值的類型(以及這些類型的序列化器)進(jìn)行操作的原因。 我們將從一個(gè)簡(jiǎn)單的示例開始,在該示例中,鍵和值只是字符串(實(shí)際上, Spring Data Redis已經(jīng)包含了一個(gè)名為StringRedisTemplate的類,但盡管如此,讓我們看一下它的一般概念)。
package com.javacodegeeks.redis;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer;@Configuration @Import( value = ConnectionConfiguration.class ) public class ApplicationConfiguration {@Bean @Autowiredpublic RedisTemplate< String, String > redisTemplate( final JedisConnectionFactory connectionFactory ) { final RedisTemplate< String, String > template = new RedisTemplate< String, String >(); template.setConnectionFactory( connectionFactory );template.setKeySerializer( new StringRedisSerializer() );template.setHashValueSerializer( new StringRedisSerializer() );template.setHashKeySerializer( new StringRedisSerializer() );template.setValueSerializer( new StringRedisSerializer() );template.setStringSerializer( new StringRedisSerializer() );return template;} }這樣,我們就可以編寫我們的第一個(gè)測(cè)試了。 我們將要?jiǎng)?chuàng)建的所有測(cè)試用例的容器是一個(gè)名為RedisStringsTestCase的類,該類大量使用Spring Test腳手架提供的測(cè)試功能。
package com.javacodegeeks.redis;import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat;import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith( SpringJUnit4ClassRunner.class ) @ContextConfiguration( classes = ApplicationConfiguration.class ) public class RedisStringsTestCase {@Autowired private RedisTemplate< String, String > template;// Out tests are going to be there }通常,對(duì)于Redis命令的每種類別(請(qǐng)參閱第2部分 , Redis命令–使用Redis命令行 ), RedisTemplate具有從“ ops ”(opsForValue,opsForList,opsForHash等)開始的fa?ade方法,該方法從字面上返回一對(duì)一的一種類別特定命令到Java方法調(diào)用的映射。 第一個(gè)測(cè)試使用SET命令存儲(chǔ)一些鍵/值,并立即使用GET命令來(lái)驗(yàn)證它是否正確存儲(chǔ)。
@Test public void testSetAndGet() {template.opsForValue().set( "mykey", "myvalue" );assertThat( template.opsForValue().get( "mykey"), equalTo( "myvalue" ) ); }為了確保測(cè)試不會(huì)失敗,我們可以使用redis-cli工具并連接到redis-host實(shí)例來(lái)檢查mykey值是否為“ myvalue ”。
圖3.驗(yàn)證mykey值是否確實(shí)存儲(chǔ)在redis-host實(shí)例上
在轉(zhuǎn)到下一個(gè)測(cè)試用例之前,需要考慮一個(gè)問(wèn)題:上一個(gè)測(cè)試完成執(zhí)行后, mykey將保留在Redis中,直到有人將其刪除為止。 這種行為可能會(huì)導(dǎo)致其他測(cè)試用例失敗,通常被認(rèn)為是不良做法。 最好使用干凈的數(shù)據(jù)庫(kù)開始每個(gè)測(cè)試,實(shí)際上我們將使它像這樣工作。 清除當(dāng)前Redis數(shù)據(jù)庫(kù)的命令是FLUSHDB 。 它不是直接由RedisTemplate公開的,但是可以通過(guò)使用execute()方法并在基礎(chǔ)RedisConnection上調(diào)用flushDb()來(lái)輕松訪問(wèn)和觸發(fā)。
@Before public void setUp() {template.execute( new RedisCallback< Void >() {@Overridepublic Void doInRedis( RedisConnection connection ) throws DataAccessException {connection.flushDb();return null;}} ); }我們將要探索的下一個(gè)操作是INCREMENT命令,該命令演示了字符串和包含數(shù)字的字符串之間的透明操作是如何進(jìn)行的。
@Test public void testSetAndIncrementAndGet() {template.opsForValue().set( "mykey", "10" );template.opsForValue().increment( "mykey", 5 );assertThat( template.opsForValue().get( "mykey"), equalTo( "15" ) ); }足夠簡(jiǎn)單,讓我們進(jìn)入更復(fù)雜的數(shù)據(jù)結(jié)構(gòu):列表,集合和排序集合。 第一個(gè)測(cè)試用例創(chuàng)建一個(gè)值列表“ a ”,“ b ”,“ c ”,“ d ”(使用RPUSH命令)。 然后它檢查列表(大小LLEN命令),請(qǐng)求由索引(該列表的最后一個(gè)元素LINDEX命令),最后從列表(彈出所述第一元件LPOP命令)。
@Test public void testPushToListAndGetElementByIndexAndPopFirstElement() {template.opsForList().rightPushAll( "mykey", "a", "b", "c", "d" );assertThat( template.opsForList().size( "mykey" ), equalTo( 4L ) );assertThat( template.opsForList().index( "mykey", 3 ), equalTo( "d" ) );assertThat( template.opsForList().leftPop( "mykey" ), equalTo( "a" ) );assertThat( template.opsForList().size( "mykey"), equalTo( 3L ) ); }該代碼看起來(lái)非常緊湊且易讀。 讓我們從列表轉(zhuǎn)到集,然后下一個(gè)測(cè)試用例創(chuàng)建一組值“ a ”,“ b ”,“ c ”,“ d ”(使用SADD命令)。 然后,它檢查集合的大小(使用SCARD命令),并詢問(wèn)“ c ”和“ e ”是否是該集合的成員(使用SISMEMBER命令)。
@Test public void testAddToSetAndCheckElementExists() {template.opsForSet().add( "mykey", "a", "b", "c", "d" );assertThat( template.opsForSet().size( "mykey" ), equalTo( 4L ) );assertThat( template.opsForSet().isMember( "mykey", "c" ), equalTo( true ) );assertThat( template.opsForSet().isMember( "mykey", "e" ), equalTo( false ) ); }組示出了全功率向上在幀間的一組操作:交叉點(diǎn)( SINTER命令),接頭( SUNION命令)和差( SDIFF命令)。 下面的測(cè)試案例通過(guò)將這些操作應(yīng)用于兩組來(lái)演示該操作。
@Test public void testIntersetOperations() {template.opsForSet().add( "mykey1", "a", "b", "c", "d" );template.opsForSet().add( "mykey2", "c", "d", "e", "f" );assertThat( template.opsForSet().intersect( "mykey1", "mykey2" ), equalTo( set( "c", "d" ) ) );assertThat( template.opsForSet().union( "mykey1", "mykey2" ), equalTo( set( "a", "b", "c", "d", "e", "f" ) ) );assertThat( template.opsForSet().difference( "mykey1", "mykey2" ), equalTo( set( "a", "b" ) ) ); }為了完成數(shù)據(jù)收集類型,我們將介紹Spring Data Redis稱為ZSets的排序集。 下面的測(cè)試用例創(chuàng)建一個(gè)排序集( ZADD命令),然后要求Redis返回按分?jǐn)?shù)從高到低排序的所有成員(帶有WITHSCORE選項(xiàng)的ZREVRANGEBYSCORE命令)。
@Test public void testAddToSortedSetAndCheckElementsAreSortedByScore() {template.opsForZSet().add( "mykey", "a", 6.15d );template.opsForZSet().add( "mykey", "b", 9.95d );template.opsForZSet().add( "mykey", "c", 8.45d );assertThat( template.opsForZSet().reverseRangeByScoreWithScores( "mykey", 0d, 10d ),equalTo( set( ( TypedTuple< String > )new DefaultTypedTuple< String >( "b", 9.95d ), ( TypedTuple< String > )new DefaultTypedTuple< String >( "a", 6.15d ), ( TypedTuple< String > )new DefaultTypedTuple< String >( "c", 8.45d ) ) ) ); }由于需要進(jìn)行通用類型轉(zhuǎn)換,因此代碼有點(diǎn)冗長(zhǎng),但通常也很簡(jiǎn)單且可讀性強(qiáng)。
最后,我們將把注意力轉(zhuǎn)移到哈希上。 Redis哈??梢员徽J(rèn)為是Java中的數(shù)據(jù)對(duì)象:屬性(或字段)及其值的容器。 下一個(gè)測(cè)試用例(使用HSET命令)創(chuàng)建具有兩個(gè)屬性(或字段),“ prop1 ”和“ prop2 ”的哈希 。 然后,它驗(yàn)證所有屬性及其值是否正確存儲(chǔ)(使用HGETALL命令),從哈希中刪除所有屬性(字段)(使用HDEL命令),并檢查它們是否確實(shí)被刪除(使用HGET命令)。
@Test public void testHashOperations() {template.opsForHash().put( "mykey", "prop1", "value1" );template.opsForHash().put( "mykey", "prop2", "value2" ); assertThat( template.opsForHash().entries( "mykey" ), equalTo( map( "prop1", "value1", "prop2", "value2" ) ) );assertThat( template.opsForHash().get( "mykey", "prop1" ), equalTo( ( Object )"value1" ) );template.opsForHash().delete( "mykey", "prop1", "prop2" );assertThat( template.opsForHash().get( "mykey", "prop1" ), equalTo( null ) ); }在本節(jié)中,我們介紹了Spring Data Redis的一些基礎(chǔ)知識(shí),并很好地理解了Redis命令如何映射到其API。 我們開發(fā)的測(cè)試用例的數(shù)量只是瞥了一眼豐富的Spring Data Redis功能集。 在接下來(lái)的三個(gè)部分中,我們將研究高級(jí)API模式:事務(wù),管道和發(fā)布/訂閱。
9.使用Spring Data Redis進(jìn)行事務(wù)
盡管Redis交易支持在一定程度上受到限制,但是在需要時(shí)它仍然是一個(gè)非常有用的功能。 為了展示Spring Data Redis如何支持Redis事務(wù)語(yǔ)義,我們將創(chuàng)建一個(gè)測(cè)試用例:
- 為兩個(gè)鍵分配一些值: mykey1 ( SET命令)和mykey2 ( SADD命令)
- 驗(yàn)證成員“ a ”不在mykey2集中( SISMEMBER命令)
- 開始監(jiān)視密鑰mykey1 ( WATCH命令)
- 啟動(dòng)事務(wù)( MULTI命令)
- 遞增mykey1 ( INCREMENT命令)
- 將新成員“ b ”添加到集合mykey2 ( SADD命令)
- 通過(guò)發(fā)出EXEC命令來(lái)完成事務(wù)
但是,我們忽略了template.execute()方法調(diào)用的返回值,它返回每個(gè)命令的結(jié)果。 在我們的測(cè)試案例中, INCREMENT的結(jié)果為15 , SADD的結(jié)果為1 。
10.使用Spring Data Redis進(jìn)行流水線
我們大部分時(shí)間訪問(wèn)Redis的方式是單個(gè)命令/響應(yīng)序列:為了發(fā)送新命令,客戶端應(yīng)等待Redis服務(wù)器返回上一個(gè)命令的結(jié)果。 但是,有一種方法可以將多個(gè)命令發(fā)送到服務(wù)器,而無(wú)需等待任何響應(yīng),最后一步即可讀取所有響應(yīng)。 這種技術(shù)稱為pipelining 。
Redis從很早的發(fā)行版開始就支持流水線化,因此無(wú)論您運(yùn)行的是哪個(gè)版本,都可以在Redis中使用流水線化(有關(guān)更多詳細(xì)信息,請(qǐng)參見http://redis.io/topics/pipelining )。
流水線可通過(guò)減少網(wǎng)絡(luò)延遲來(lái)顯著提高應(yīng)用程序的性能。 但是有一個(gè)陷阱:當(dāng)任何客戶端使用流水線技術(shù)發(fā)送命令時(shí),服務(wù)器將被迫將響應(yīng)排隊(duì)在內(nèi)存中。 如果需要通過(guò)管道傳遞大量命令,則最好將這些命令發(fā)送到給定的合理數(shù)量(以便將它們拆分為多個(gè)管道)。 性能將幾乎相同,但使用的額外內(nèi)存將受到限制。
以下測(cè)試案例演示了如何使用Spring Data Redis進(jìn)行流水線操作。 我們將使用流水線發(fā)送100條命令,并通過(guò)將計(jì)數(shù)器的值與預(yù)期值進(jìn)行比較來(lái)驗(yàn)證是否已發(fā)送并處理了所有命令。
@Test public void testPipelining() {template.opsForValue().set( "mykey1", "10" ); template.executePipelined( new RedisCallback< Object >() {@Overridepublic Object doInRedis(RedisConnection connection) throws DataAccessException {for( int i = 0; i < 100; ++i ) {template.opsForValue().increment( "mykey1", 1 );}return null;}} );assertThat( template.opsForValue().get( "mykey1"), equalTo( "110" ) ); }與Redis事務(wù)測(cè)試用例一樣,我們忽略了template.executePipelined()方法調(diào)用的返回值,但它返回每個(gè)命令的結(jié)果(總共100個(gè)結(jié)果)。 另外,如果您想知道為什么我們從RedisCallback返回null ,則是有原因的:此返回值將被響應(yīng)中的實(shí)際值(收到時(shí))覆蓋,因此該回調(diào)不允許返回非null值(請(qǐng)參閱請(qǐng)?jiān)L問(wèn)http://docs.spring.io/spring-data/data-redis/docs/1.2.0.RELEASE/reference/html/redis.html#pipeline了解更多信息)。
11.使用Spring Data Redis發(fā)布/訂閱
Redis支持發(fā)布/訂閱消息傳遞范例, Spring Data Redis也提供對(duì)此功能的全面支持。
從本質(zhì)上講,發(fā)布/訂閱消息傳遞至少涉及兩個(gè)參與者:發(fā)布消息的發(fā)布者和偵聽發(fā)布者的消息的訂閱者(通常是多對(duì)多關(guān)系,但我們已將其簡(jiǎn)化為單個(gè)發(fā)布者/訂閱者模型)。
為此開發(fā)一個(gè)健壯的測(cè)試用例可能會(huì)有些棘手。 發(fā)布是很容易的部分,但是在特定渠道(或模式)上偵聽消息需要做一些工作。 我們首先定義稱為RedisMessageListener的訂戶類。 它不會(huì)做很多,但會(huì)計(jì)算到目前為止已收到的所有消息。
package com.javacodegeeks.redis;import java.util.concurrent.atomic.AtomicInteger;import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.connection.MessageListener;public class RedisMessageListener implements MessageListener {private AtomicInteger count = new AtomicInteger( 0 ); @Overridepublic void onMessage(Message message, byte[] pattern) {count.incrementAndGet();}public int getCount() {return count.get();} }接下來(lái),我們應(yīng)該使用RedisMessageListener和RedisMessageListenerContainer其他bean擴(kuò)展配置。 后者的作用非常重要:它將聽眾和他們正在收聽的頻道粘合在一起。 PubsubConfiguration我們開始所需的最低配置( PubsubConfiguration )(請(qǐng)注意,我們正在導(dǎo)入之前創(chuàng)建的ApplicationConfiguration )。
package com.javacodegeeks.redis;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.data.redis.connection.MessageListener; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.listener.ChannelTopic; import org.springframework.data.redis.listener.RedisMessageListenerContainer;@Configuration @Import( value = ApplicationConfiguration.class ) public class PubsubConfiguration {@Bean @Autowiredpublic RedisMessageListenerContainer container( final JedisConnectionFactory connectionFactory ) { final RedisMessageListenerContainer container = new RedisMessageListenerContainer();container.setConnectionFactory( connectionFactory );container.addMessageListener( listener(), new ChannelTopic( "test-channel" ) );return container;}@Beanpublic MessageListener listener() {return new RedisMessageListener();} }為了提供一些背景信息,我們將偵聽器附加到名為“ test-channel ” 的通道 ,這就是我們要向其發(fā)布消息的通道。
發(fā)布/訂閱消息傳遞通信本質(zhì)上是異步的,這是在開發(fā)這種功能的測(cè)試用例時(shí)的又一個(gè)復(fù)雜問(wèn)題。 訂戶將不會(huì)立即接收消息,但是會(huì)有些延遲。 如果要連接到本地計(jì)算機(jī)上運(yùn)行的Redis服務(wù)器,則可能需要花費(fèi)毫秒,但是如果要訪問(wèn)云中的某個(gè)實(shí)例,則可能需要一段時(shí)間。 一種可能的處理方法是引入某種合理的延遲,以便給訂戶一些時(shí)間來(lái)接收所有消息(這也是我們?cè)跍y(cè)試案例中使用的技巧)。
package com.javacodegeeks.redis;import static com.jayway.awaitility.Awaitility.await; import static java.util.concurrent.TimeUnit.SECONDS; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat;import java.util.concurrent.Callable;import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith( SpringJUnit4ClassRunner.class ) @ContextConfiguration( classes = PubsubConfiguration.class ) public class RedisPublishSubscriberTestCase {@Autowired private RedisTemplate< String, String > template;@Autowired private RedisMessageListener listener;@Testpublic void testPublishSubscribe() {assertThat( listener.getCount(), equalTo( 0 ) );template.convertAndSend( "test-channel", "Test Message 1!" );template.convertAndSend( "test-channel", "Test Message 2!" );template.convertAndSend( "test-channel", "Test Message 3!" );await().atMost( 1, SECONDS ).until(new Callable< Integer >() { @Overridepublic Integer call() throws Exception {return listener.getCount();}}, equalTo( 3 ) );} }這個(gè)新的測(cè)試案例使用我們的PubsubConfiguration類實(shí)例化Spring測(cè)試上下文。 在測(cè)試用例本身中,我們將三個(gè)消息發(fā)布到Redis服務(wù)器上的“ test-channel ” 通道 。 然后,我們給消息偵聽器一些時(shí)間(但不超過(guò)一秒),以消耗通道上發(fā)布的所有消息。 之后,我們期望偵聽器收到所有3條消息,這就是我們最后要驗(yàn)證的內(nèi)容。
12.結(jié)論
Redis的發(fā)展非常Swift。 跟上它的所有新功能和命令非常困難。 這樣,您可能會(huì)發(fā)現(xiàn)Spring Data Redis尚不支持最新Redis發(fā)行版中提供的某些最新功能(甚至Java客戶端也需要一些時(shí)間來(lái)添加對(duì)它的支持)。 一個(gè)很好的例子是Redis Cluster, Spring Data Redis尚不支持它。
這是Redis教程的最后一部分,但這只是通往Redis世界的開始。 如果您想關(guān)注Redis的最新發(fā)展,可以參考以下兩個(gè)資源:
- http://antirez.com/:Redis的創(chuàng)建者Salvatore Sanfilippo的博客
- http://aphyr.com/posts/283-call-me-maybe-redis :關(guān)于Redis群集可用性的非常有用的見解
翻譯自: https://www.javacodegeeks.com/2015/09/spring-data-and-redis.html
總結(jié)
以上是生活随笔為你收集整理的Spring数据和Redis的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: java 模块化osgi_OSGi简介–
- 下一篇: 安卓java开发环境(安卓java开发)