1 Spring Boot使用Jedis客户端连接Redis哨兵集群。Jedis是一个Java Redis客户端,它支持连接Redis哨兵集群。
2 Jedis客户端会向Redis哨兵集群发送SENTINEL get-master-addr-by-name命令,获取当前Redis主节点的IP地址和端口号。
3 Jedis客户端会使用获取到的IP地址和端口号连接Redis主节点,并发送PING命令测试连接是否正常。
4 如果连接正常,Jedis客户端会将连接信息保存在连接池中,以便后续使用。
5 如果连接失败,Jedis客户端会向Redis哨兵集群发送SENTINEL get-master-addr-by-name命令,获取新的Redis主节点的IP地址和端口号,然后重复步骤2-4。
6 如果Redis主节点发生故障,Redis哨兵会自动将从节点升级为主节点,并通知Jedis客户端更新连接信息。
总之,Spring Boot连接Redis哨兵集群的原理是通过Jedis客户端向Redis哨兵集群发送命令获取当前Redis主节点的IP地址和端口号,然后使用获取到的信息连接Redis主节点。如果连接失败,Jedis客户端会重新获取新的Redis主节点的IP地址和端口号,直到连接成功为止。如果Redis主节点发生故障,Redis哨兵会自动将从节点升级为主节点,并通知Jedis客户端更新连接信息。
此外,我还讨论过较为常见的基于服务器的数据存储,比如MongoDB和CouchDB。每个数据存储都有其优势和劣势,特别是当应用于特定领域时。本期的Java开发20关注的是Redis,一种轻量级键值对数据存储。多数NoSQL实现本质上都是键值对,但是Redis支持非常丰富的值集,其中包括字符串、列表、集以及散列。因此,Redis通常被称为数据结构服务器。Redis也以异常快速而闻名,这使得它成为某一特定类型使用案例的最优选择。当我们想要了解一种新事物时,将其同熟知的事物进行比较可能会有所帮助,因此,我们将通过对比其与memcached的相似性以开启Redis探索之旅。接着我们将介绍Redis的主要功能,这些功能可以使其在某些应用场景可以胜过memcached。最后我将向您展示如何将Redis作为一个传统数据存储用于模型对象。Redis和memcachedMemcached是一个众所周知的内存对象缓存系统,通过将目标键和值导入内存缓存运行。因此,Memcached能回避读取磁盘时发生的I/O成本问题。在Web应用程序和数据库之间粘贴memcached时会产生更好的读取性能。因此,对于那些需要快速数据查询的应用程序,Memcached是一个不错的选择。其中的一个例子为股票查询服务,需要另外访问数据库获取相对静态数据,如股票名称或价格信息。MemcacheDB将Redis与memcached相比较并不公平,它与MemcacheDB相比要好的多,MemcacheDB是一个分布式键值对存储系统,专为数据持久化而设计。MemcacheDB与Redis较为相似,其新增优势可以使其轻松地与memcached实现的客户端进行通信。但是memcached也有其局限性,其中一个事实就是它所有的值均是简单的字符串。Redis作为memcached的替代者,支持更加丰富的功能集。一些基准(benchmarks)也表明Redis的速度要比memcached快很多。Redis提供的丰富数据类型使其可以在内存中存储更为复杂的数据,这是使用memcached无法实现的。同memcached不一样,Redis可以持久化其数据。Redis解决了一个重大的缓存问题,而其丰富的功能集又为其找到了其他用途。由于Redis能够在磁盘上存储数据以及跨节点复制数据,因而可以作为数据仓库用于传统数据模式(也就是说,您可以使用Redis,就像使用RDBMS一样)。Redis还经常被用作队列系统。在本用例中,Redis是备份和工作队列持久化存储(利用Redis的列表类型)的基础。GitHub是以此种方法使用Redis的大规模基础架构示例准备好Redis,立即开始!要开始使用Redis,您需要访问它,可以通过本地安装或者托管供应商来实现访问。如果您使用的MAC,安装过程可能就不那么简单。如果您使用的是Windows,您需要先安装Cygwin。如果您正在寻找一个托管供应商,Redis4You拥有一个免费计划。不管您以何种方式访问,您都能够根据本文下列示例进行 *** 作,但是我需要指出的是,使用一个托管供应商进行缓存可能并不是很好的缓存解决方案,因为网络延迟可能会抵消任何性能优势。您需要通过命令与Redis进行交互,这就是说,这里没有SQL类查询语言。使用Redis工作非常类似于使用传统map数据结构,即所有的一切都拥有一个键和一个值,每个值都有多种与之关联的数据类型。每个数据类型都有其自己的命令集。例如,如果您计划使用简单数据类型,比如某种缓存模式,您可以使用命令set和get。您可以通过命令行shell与一个Reids实例进行交互。还有多个客户端实现,可以以编程方式与Redis进行交互。清单1展示了一个使用基础命令的简单命令行shell交互:清单1使用基础的Redis命令redis127001:6379>setpageregistrationOKredis127001:6379>keys1)"foo"2)"page"redis127001:6379>getpage"registration"在这里,我通过set命令将键"page"与值"registration"相关联。接着,我发出keys命令(后缀表示我想看到所有可用的实例键。keys命令显示有一个page值和一个foo,我可以通过get命令检索到与一个键关联的值。请记住,使用get检索到的值只能是一个字符串。如果一个键的值是一个列表,那么您必须使用一个特定列表的命令来检索列表元素。(注意,有可以查询值类型的命令)。Java与Jedis集成对于那些想要将Redis集成到Java应用程序的编程人员,Redis团队建议使用一个名为Jedis的项目,Jedis是一个轻量级库,可以将本地Redis命令映射到Java方法。例如Jedis可以获取并设置简单值,如清单2所示:清单2Java代码中的基础Redis命令JedisPoolpool=newJedisPool(newJedisPoolConfig(),"localhost");Jedisjedis=poolgetResource();jedisset("foo","bar");Stringfoobar=jedisget("foo");assertfoobarequals("bar");poolreturnResource(jedis);pooldestroy();在清单2中,我配置了一个连接池并捕获连接,(与您在典型JDBC场景中的 *** 作非常相似)然后我在清单的底部设置了返回 *** 作。在连接池逻辑之间,我设置了值"bar"和键"foo",这是我通过get命令检索到的。与memcached类似,Redis允许您将过期(expiration)时间关联到一个值。因此我设置了这样一个值(比如,股票临时交易价格),最终将从Redis缓存中清除掉。如果我想在Jedis中设置一个过期时间,需要在发出set调用之后将其和一个过期时间关联。如清单3所示:清单3Redis值可以设置为终止jedisset("gone","daddy,gone");jedisexpire("gone",10);Stringthere=jedisget("gone");assertthereequals("daddy,gone");Threadsleep(4500);StringnotThere=jedisget("gone");assertnotThere==null;在清单3中,我使用了一个expire调用将"gone"的值设置为在10秒钟内终止。调用Threadsleep之后,"gone"的get调用会返回null。Redis中的数据类型使用Redis数据类型,比如列表和散列需要专用命令用法。例如,我可以通过为键附加值来创建列表。
Spring Boot是目前非常流行的Java Web开发框架,Redis是非关系型数据库的一种,以键值对的形式存储。Spring对Redis的支持是通过Spring Data Redis来实现的,给我们提供了RedisTemplate和StringRedisTemplate两种模板来 *** 作数据。Spring Boot框架也提供了对Redis的支持,下面我们来讲一下Spring Boot框架整合Redis的步骤。
工具/材料IntelliJ IDEA
01Spring Boot整合Redis我们需要添加依赖的jar包,spring-boot-starter-data-redis中包含spring和redis相关的jar包,jedis作为redis的客户端也需要添加到工程中,Spring Boot的版本信息在父pom中已指定,子模块中的spring相关的jar包无需另外指定。
<dependency>
<groupId>orgspringframeworkboot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redisclients</groupId>
<artifactId>jedis</artifactId>
<version>300-m1</version>
</dependency>
02Spring Boot会根据applicationproperties中的配置对Redis的属性进行自动配置,并注入到RedisProperties类中。在applicationproperties配置文件中这些属性都是以springredis为前缀的,值得注意的是在Spring Boot 15x版本中默认的Redis客户端是jedis,因此在配置文件中无需指定,如下图所示。
03Spring Boot 15x版本的整合配置网上可以搜索大量的文章,然而Spring Boot 2x版本的整合资料却非常少,甚至提供的配置不能正常使用,因此本文主要讲解Spring Boot 2x整合Redis以及Redis的使用情况。spring-boot 2x版本有jedis和lettuce两种客户端,因此我们必须要去指定使用哪一种客户端,两个客户端的配置如下图所示,本文使用的是Jedis客户端连接池,具体的配置如下。
# Redis数据库索引(默认为0)
springredisdatabase=0
# Redis服务器地址
springredishost=127001
# Redis服务器连接端口
springredisport=6379
# Redis服务器连接密码(默认为空)
springredispassword=xylx1t!@#
# 配置jedis连接池
# 连接池最大连接数(使用负值表示没有限制)
springredisjedispoolmax-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
springredisjedispoolmax-wait=-1ms
# 连接池中的最大空闲连接
springredisjedispoolmax-idle=8
# 连接池中的最小空闲连接
springredisjedispoolmin-idle=0
# 连接超时时间(毫秒)
springredistimeout=5000ms
由配置我们可以看到spring-boot 2x版本时间设置需要加单位ms,因为参数的类型为Duration。另外springredistimeout尽量不要配置0,否则可能会出现iolettucecoreRedisCommandTimeoutException: Command timed out超时错误。
04配置文件编辑完成后,我们开始编写代码实现Redis数据的存储和读取。我们创建一个RedisUtil工具类,该类使用@Component注解表示交由Spring管理,StringRedisTemplate是Spring提供的,可以使用@Autowired注解直接注入,接下来便可以书写存和取的代码了。
@Component
public class RedisUtil {
@Autowired
private StringRedisTemplate redisTemplate;
/
存字符串
@param key 缓存键
@param value 缓存值
@param expireTime 过期时间(s)
/
public void setString(String key, String value, int expireTime){
ValueOperations<String, String> ops = redisTemplateopsForValue();
if (expireTime != 0) {
opsset(key, value, expireTime, TimeUnitSECONDS);
} else {
opsset(key,value);
}
}
/
取字符串
@param key 缓存键
@return 缓存值
/
public String getString(String key){
ValueOperations<String, String> ops = thisredisTemplateopsForValue();
return opsget(key);
}
05接下来我们编写Controller层代码去调用RedisUtil工具类,实现数据的存储和读取,代码比较简单可以参考下图。若想验证Redis是否可用,还需要编写启动类,如下图所示。
06由上图可看到我们编写了一个post请求用于存储字符串,get请求用于取出字符串。启动类通过main方法启动应用,接下来我们使用postman去模拟浏览器调用post和get请求,由下图可以看到Redis存储的数据成功被取出。
07接下来我们介绍Jedis,这是一个封装了Redis的客户端,在Spring Boot整合Redis的基础上,可以提供更简单的API *** 作。因此我们需要配置JedisPool的Bean,代码如下,其中@Configuration注解表明这是一个配置类,我们在该类中注入RedisProperties,并且使用@Bean注解指定JedisPool。
@Configuration
public class RedisConfiguration {
@Autowired
private RedisProperties properties;
@Bean
public JedisPool getJedisPool(){
JedisPoolConfig config = new JedisPoolConfig();
configsetMaxIdle(propertiesgetJedis()getPool()getMaxIdle());
configsetMaxTotal(propertiesgetJedis()getPool()getMaxActive());
configsetMaxWaitMillis(propertiesgetJedis()getPool()getMaxWait()toMillis());
JedisPool pool = new JedisPool(config,propertiesgetHost(),
propertiesgetPort(),100,
propertiesgetPassword(), propertiesgetDatabase());
return pool;
}
}
08接下来我们编辑JedisUtil工具类,通过SpringBoot容器的@Component注解来自动创建,并且注入JedisPool,使用jedisPoolgetResource()方法来获取Jedis,并最终实现 *** 作redis数据库,其代码如下。
@Component
public class JedisUtil {
@Autowired
JedisPool jedisPool;
//获取key的value值
public String get(String key) {
Jedis jedis = jedisPoolgetResource();
String str = "";
try {
str = jedisget(key);
} finally {
try {
jedisclose();
} catch (Exception e) {
eprintStackTrace();
}
}
return str;
}
public String set(String key, String value) {
Jedis jedis = jedisPoolgetResource();
String str = "";
try {
str = jedisset(key, value);
} finally {
try {
jedisclose();
} catch (Exception e) {
eprintStackTrace();
}
}
return str;
}
}
09JedisUtil工具类编写完成后,我们修改之前的RedisController,并注入JedisUtil,代码如下图所示。然后再用postman分别调用post和get接口,我们可以看到成功取到了新的key的value值。
特别提示在Spring Boot整合Redis前本机需安装Redis,另外可以使用RedisDesktopManager这个Redis这个桌面管理工具查看Redis中的数据。
item表示迭代的参数 比如你的这个数组通过requestsetAttribute("array",xxx)放入了这个里,迭代的内容就是${requestScopearray}
var 相当于一个引用
下面你就可以用
${arrayxxx1}
${arrayxxx2}
将数组里的值输出出来了
Caused by: orgspringframeworkdataredisRedisConnectionFailureException: Could not get a resource from the pool; nested exception is redisclientsjedisexceptionsJedisConnectionException: Could not get a resource from the pool
at orgspringframeworkdataredisconnectionjedisJedisExceptionConverterconvert(JedisExceptionConverterjava:67)
at orgspringframeworkdataredisconnectionjedisJedisExceptionConverterconvert(JedisExceptionConverterjava:41)
at orgspringframeworkdataredisPassThroughExceptionTranslationStrategytranslate(PassThroughExceptionTranslationStrategyjava:37)
at orgspringframeworkdataredisconnectionjedisJedisClusterConnectionconvertJedisAccessException(JedisClusterConnectionjava:3999)
at orgspringframeworkdataredisconnectionjedisJedisClusterConnectionsetEx(JedisClusterConnectionjava:717)
at orgspringframeworkdatarediscoreDefaultValueOperations 11doInRedis(DefaultValueOperationsjava:186)
at orgspringframeworkdatarediscoreRedisTemplateexecute(RedisTemplatejava:207)
at orgspringframeworkdatarediscoreRedisTemplateexecute(RedisTemplatejava:169)
at orgspringframeworkdatarediscoreAbstractOperationsexecute(AbstractOperationsjava:91)
at orgspringframeworkdatarediscoreDefaultValueOperationsset(DefaultValueOperationsjava:182)
at iorenrencommonutilsSuppleDataUtilsprovideIcc(SuppleDataUtilsjava:107)
at iorenrenmodulesjobtaskTaskIcctaskFour(TaskIccjava:242)
10 common frames omitted
Caused by: redisclientsjedisexceptionsJedisConnectionException: Could not get a resource from the pool
at redisclientsutilPoolgetResource(Pooljava:53)
at redisclientsjedisJedisPoolgetResource(JedisPooljava:226)
at redisclientsjedisJedisSlotBasedConnectionHandlergetConnectionFromSlot(JedisSlotBasedConnectionHandlerjava:66)
at redisclientsjedisJedisClusterCommandrunWithRetries(JedisClusterCommandjava:116)
at redisclientsjedisJedisClusterCommandrunWithRetries(JedisClusterCommandjava:141)
at redisclientsjedisJedisClusterCommandrunWithRetries(JedisClusterCommandjava:141)
at redisclientsjedisJedisClusterCommandrunWithRetries(JedisClusterCommandjava:141)
at redisclientsjedisJedisClusterCommandrunWithRetries(JedisClusterCommandjava:141)
at redisclientsjedisJedisClusterCommandrunBinary(JedisClusterCommandjava:60)
at redisclientsjedisBinaryJedisClustersetex(BinaryJedisClusterjava:268)
at orgspringframeworkdataredisconnectionjedisJedisClusterConnectionsetEx(JedisClusterConnectionjava:715)
18 common frames omitted
Caused by: redisclientsjedisexceptionsJedisConnectionException: javanetSocketTimeoutException: connect timed out
at redisclientsjedisConnectionconnect(Connectionjava:207)
at redisclientsjedisBinaryClientconnect(BinaryClientjava:93)
at redisclientsjedisBinaryJedisconnect(BinaryJedisjava:1767)
at redisclientsjedisJedisFactorymakeObject(JedisFactoryjava:106)
at orgapachecommonspool2implGenericObjectPoolcreate(GenericObjectPooljava:868)
at orgapachecommonspool2implGenericObjectPoolborrowObject(GenericObjectPooljava:435)
at orgapachecommonspool2implGenericObjectPoolborrowObject(GenericObjectPooljava:363)
at redisclientsutilPoolgetResource(Pooljava:49)
28 common frames omitted
Caused by: javanetSocketTimeoutException: connect timed out
at javanetDualStackPlainSocketImplwaitForConnect(Native Method)
at javanetDualStackPlainSocketImplsocketConnect(DualStackPlainSocketImpljava:85)
at javanetAbstractPlainSocketImpldoConnect(AbstractPlainSocketImpljava:350)
at javanetAbstractPlainSocketImplconnectToAddress(AbstractPlainSocketImpljava:206)
at javanetAbstractPlainSocketImplconnect(AbstractPlainSocketImpljava:188)
at javanetPlainSocketImplconnect(PlainSocketImpljava:172)
at javanetSocksSocketImplconnect(SocksSocketImpljava:392)
at javanetSocketconnect(Socketjava:589)
at redisclientsjedisConnectionconnect(Connectionjava:184)
35 common frames omitted
最近在本地测试通过springboot基础redis的方式连接redis集群,启动的时候没有报错。
到时当执行保存,查询到redis的 *** 作的时候,报上面的错误。
首先看到错误信息,先是connect timed out后,再出现的Could not get a resource from the pool错误。一开始排查这个错误,由于使用的是默认连接池,debug的时候,也看到有装载连接池,为啥报错呢。本地通过redis-manger工具,连接集群地址也可以连接访问。从日志也看不出还有其他的错误信息。
于是自己写了main方法直接 *** 作JedisCluster的方式来测试,出现的错误跟上面全文一样。用抓包工具wireshark过滤集群的地址,查看连接的消息也是正常的。
另外一个项目使用的reddison客户端,于是测试了deamo用reddison来连接集群地址,启动的时候,发现报如下错误
乖乖,恍然大悟;102871731地址是服务器的内网地址,本机是连不上内网地址的,只有通过外网地址连接。而我配置的是外网地址。然而,客户端在集群同步和心跳检查的时候,是拉取的集群信息,redis集群信息里面的节点的信息
配置的是内网地址。客户端就通过这个内网地址来同步信息了。
再次同wireshark过滤集群102871731,发现有大量的超时重传的包,也没有响应。
以上就是关于springboo连接redis哨兵集群原理全部的内容,包括:springboo连接redis哨兵集群原理、scala怎样创建redis集群连接池、Spring Boot如何整合Redis等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)