redis开发与运维-redis04-redis客户端Jedis与连接池及客户端异常模拟

文章目录

  • 【README】
  • 【1】redis客户端通信协议
  • 【2】java客户端Jedis连接redis集群
    • 【2.1】Jedis基本用法
    • 【2.2】Jedis操作5种数据类型代码实践
    • 【2.3】Jedis使用序列化api操作
      • 【2.3.1】操作Jedis字节数组api代码实践
  • 【3】Jedis连接池
    • 【3.1】Jedis连接池JedisPool代码实践
      • 【3.1.1】池化Jedis对象#close方法解析
    • 【3.2】Jedis连接池JedisPool配置属性概览
  • 【4】redis客户端常见异常总结(共计8个)
    • 【4.1】问题1-无法从连接池获取jedis连接
      • 【4.1.1】模拟无法从连接池获取连接
    • 【4.2】问题2-客户端读写超时
      • 【4.2.1】模拟客户端读写超时场景
    • 【4.3】问题3-客户端连接超时
      • 【4.3.1】客户端连接超时场景
    • 【4.4】问题4-客户端缓冲区异常
      • 【4.4.1】模拟客户端缓冲区异常场景
    • 【4.5】问题5-lua脚本正在执行(仅了解)
    • 【4.6】redis正在加载持久化文件(仅了解)
    • 【4.7】redis使用的内存超过maxmemory设置
      • 【4.7.1】模拟redis使用的内存超过maxmemory设置
    • 【4.8】客户端连接数过大
      • 【4.8.1】模拟客户端连接数过大异常

【README】

本文总结自《redis开发与运维》,作者付磊,张益军,墙裂推荐;

  • 本文使用的redis版本是 7.0.15 ;
  • 本文主要介绍java的redis客户端Jedis及redis连接池;
  • 代码参见: https://github.com/TomJourney/redisDiscover/tree/master


【1】redis客户端通信协议

1)redis客户端介绍:

  • redis客户端与服务器之间的通信协议是在tcp协议上建立的;
  • redis制定了 RESP(REdis Serialization Protocol, redis序列化协议)实现客户端与服务器的交互,该协议简单高效,能够被机器解析,也对开发者友好;

2)RESP协议的报文演示:

[root@centos211 ~]# telnet 192.168.163.211 6379
Trying 192.168.163.211...
Connected to 192.168.163.211.
Escape character is '^]'.# 新增或更新key
set user2 tom2
+OK# 命令错误
sethx^H^H
-ERR unknown command 'set', with args beginning with: # 自增key
incr counter
:1# 获取key值
get user2
$4
tom2# 设置或更新多个key-value
mset user3 tom3 user4 tom4
+OK# 获取多个key的value
mget user3 user4
*2  # 显然,*2表示key的个数 
$4  # $4 表示值的长度
tom3
$4
tom4


【2】java客户端Jedis连接redis集群

【2.1】Jedis基本用法

1)引入jedis的maven依赖

<!-- https://mvnrepository.com/artifact/redis.clients/jedis --><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>5.2.0</version></dependency>

2)新建jedis简单客户端操作redis

public class TomeJedisClient01Main {public static void main(String[] args) {long start = System.currentTimeMillis();Jedis jedis = null;try {jedis = new Jedis("192.168.163.211", 6379, 10000, 3000);jedis.set("tom:jedis:user1", "tom1");System.out.println(jedis.get("user1"));} catch (Exception e) {e.printStackTrace(); // 真实业务不要这么写} finally {if (Objects.nonNull(jedis)) {// 记得关闭redis连接jedis.close();}System.out.println("耗时(秒)=" + (System.currentTimeMillis() - start) / 1000);}}
}

【运行结果】

tom1
耗时(秒)=0


【2.2】Jedis操作5种数据类型代码实践

1)Jedis操作5种数据类型代码实践:

/*** @author Tom* @version 1.0.0* @ClassName TomeJedisClient02Main.java* @Description jedis操作5种数据类型* @createTime 2024年12月25日 09:30:00*/
public class TomeJedisClient02Main {public static void main(String[] args) {// 10000为连接超时时间, 3000为写入redis超时时间,仅演示,生产环境不要这么写 Jedis jedis = new Jedis("192.168.163.211", 6379, 10000, 3000);try {oprFiveTypeKey(jedis);} catch (Exception e) {e.printStackTrace();} finally {if (Objects.nonNull(jedis)) {jedis.close();}}}private static void oprFiveTypeKey(Jedis jedis) {System.out.println("\n========== 字符串类型 ==========");// 1 字符串类型// 实际可以是字符串(简单字符串,复杂字符串如json),数字(整数,浮点数),二进制(图片,音视频),值最大不超过512Mjedis.set("name01", "tom01");System.out.println(jedis.get("name01")); // tom01// 1.1 数字System.out.println(jedis.incr("counter01")); // 4System.out.println(jedis.get("counter01")); // 4System.out.println("\n========== hash类型 ==========");// 2 hash类型// 哈希类型定义:指键的值本身又是一个键值对结构。 如value={{field1, value1}, {field2, value2}}jedis.hset("person", "name", "tom01");jedis.hset("person", "addr", "chengdu");System.out.println(jedis.hget("person", "name")); // tom01System.out.println(jedis.hgetAll("person"));// {name=tom01, addr=chengdu}System.out.println("\n========== list有序列表 ==========");// 3 list有序列表// 列表类型定义: 用来存储多个有序的字符串, 如a,b,c这3个元素从左到右组成了一个有序列表jedis.del("userList"); // 先删除keyjedis.rpush("userList", "tom01", "tom02", "tom03");System.out.println(jedis.lrange("userList", 0, -1)); // [tom01, tom02, tom03]System.out.println("\n========== set无序集合 ==========");// 4 set无序集合// 集合类型定义:集合类型用于保存多个字符串元素,但不允许重复元素,且元素无序,不能通过通过下标获取元素;jedis.sadd("userSet", "tom01", "tom02", "tom03");System.out.println(jedis.spop("userSet")); // tom03System.out.println(jedis.smembers("userSet"));// [tom02, tom01]System.out.println("\n========== zset有序集合 ==========");// 5 zset有序集合jedis.zadd("userSortedSet", 3, "tom03");jedis.zadd("userSortedSet", 2, "tom02");jedis.zadd("userSortedSet", 1, "tom01");System.out.println(jedis.zrangeByScore("userSortedSet", 2, 3)); // [tom02, tom03]}
}


【2.3】Jedis使用序列化api操作

1)引入序列化与反序列化:TomeJedisClient02Main类全部使用java字符串格式key-value操作redis,能够覆盖一部分业务场景;此外,有一些业务场景需要使用字节格式存储(方便加解密),如用户token

2)Jedis本身没有提供序列化工具,开发者需要自己引入序列化工具,如xml,Json,谷歌的Protobuf,facebook的Thrift等;本文选择Protobuf;



【2.3.1】操作Jedis字节数组api代码实践

1)protostuff是 Protobuf的java客户端,maven依赖如下:

<!-- https://mvnrepository.com/artifact/io.protostuff/protostuff-core --><dependency><groupId>io.protostuff</groupId><artifactId>protostuff-core</artifactId><version>1.8.0</version></dependency><!-- https://mvnrepository.com/artifact/io.protostuff/protostuff-runtime --><dependency><groupId>io.protostuff</groupId><artifactId>protostuff-runtime</artifactId><version>1.8.0</version></dependency>

2)使用Jedis字节数组api代码实践

【TomeJedisClientMainUsingSerialization】

public class TomeJedisClientMainUsingSerialization {public static void main(String[] args) {Jedis jedis = new Jedis("192.168.163.211", 6379, 10000, 3000);byte[] keyByteArr = ProtostuffSerializationUtils.serialize("car01");try {// 序列化后设置redis键值对jedis.set(keyByteArr, ProtostuffSerializationUtils.serialize(Car.build(2, "tesla", "shanghai")));// 根据key获取redis值并反序列化Car deserializeCar = ProtostuffSerializationUtils.deserialize(jedis.get(keyByteArr), Car.class);System.out.println(deserializeCar);} catch (Exception e) {throw new IllegalStateException(e.getMessage(), e);} finally {if (Objects.nonNull(jedis)) {jedis.close();}}}
}

【日志】

Car{id=2, name='tesla', birthAddr='shanghai'}

【ProtostuffSerializationUtils】Protobuf序列化工具

public class ProtostuffSerializationUtils {// 缓冲区private static LinkedBuffer BUFFER = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);//Schema缓存private static final Map<String, Schema<?>> SCHEMA_CACHE = new ConcurrentHashMap<>();public static <T> byte[] serialize(T object) {Class<T> clazz = (Class<T>) object.getClass();try {return ProtostuffIOUtil.toByteArray(object, getSchemaInstance(clazz), BUFFER);} catch (Exception e) {throw new IllegalStateException(e);} finally {BUFFER.clear();}}public static <T> T deserialize(byte[] data, Class<T> clazz) {Schema<T> schemaInstance = getSchemaInstance(clazz);T object = schemaInstance.newMessage();ProtostuffIOUtil.mergeFrom(data, object, schemaInstance);return object;}private static <T> Schema<T> getSchemaInstance(Class<T> clazz) {return (Schema<T>) SCHEMA_CACHE.computeIfAbsent(clazz.getName(), x -> RuntimeSchema.getSchema(clazz));}private ProtostuffSerializationUtils() {// do nothing.}
}

【Car】javabean

public class Car {/*** 编号*/private int id;/*** 名称*/private String name;/*** 产地*/private String birthAddr;public static Car build(int id, String name, String birthAddr) {Car car = new Car();car.id = id;car.name = name;car.birthAddr = birthAddr;return car;}@Overridepublic String toString() {return "Car{" +"id=" + id +", name='" + name + '\'' +", birthAddr='" + birthAddr + '\'' +'}';}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getBirthAddr() {return birthAddr;}public void setBirthAddr(String birthAddr) {this.birthAddr = birthAddr;}
}


【3】Jedis连接池

1)问题与解决方法:

  • 问题:上述章节2,redis客户端Jedis使用直连方式连接到redis服务器;直连指的是Jedis每次都会新建tcp连接,使用后立即断开;若第2次使用Jedis,则又会重新建立tcp连接;每次使用Jedis都建立连接,网络io开销多,影响系统性能;
  • 解决方法:使用连接池管理Jedis连接; 应用启动时,预先初始化Jedis连接并放到连接池JedisPool中;每次要连接Redis,直接从池中获取Jedis对象,用完之后把池化Jedis连接归还给Jedis连接池;

在这里插入图片描述

2)Jedis直连与连接池优缺点对比

优点缺点
直连简单方便,适用于少量长期连接的场景1)存在每次新建或关闭tcp连接,网络io成本高;
2)连接数量无法控制,可能会导致连接泄露;
3)Jedis对象线程不安全;
连接池1)无需每次连接都生成Jedis对象,降低网络io;
2)使用连接池的形式保护与控制资源的使用;
与直连相比,连接池使用相对麻烦;连接池资源的管理需要参数来保证,
若连接池参数设置不合理,可能产生其他问题;


【3.1】Jedis连接池JedisPool代码实践

1)Jedis提供了JedisPool实现连接池, 同时使用Apache的通用对象池工具common-pool作为资源的管理工具; 代码实践如下。

【PooledJedisMain】池化redis测试入口类

public class PooledJedisMain {public static void main(String[] args) {PooledJedisFactory busiJedisFactoryUsingPool = PooledJedisFactory.build();// 从jedis连接池获取jedis对象Jedis jedis = busiJedisFactoryUsingPool.getJedis();System.out.println(jedis);try {// 执行操作jedis.set("user01", "zhagnsan01");System.out.println(jedis.get("user01"));// 执行操作jedis.set("user02", "zhagnsan02");System.out.println(jedis.get("user02"));} catch (Exception e) {throw new IllegalStateException(e.getMessage(), e);} finally {if (Objects.nonNull(jedis)) {// JedisPool连接池返回的Jedis对象,其close方法不是关闭连接,而是归还给连接池jedis.close();}}}
}

【日志】

Jedis{Connection{DefaultJedisSocketFactory{192.168.163.211:6379}}}
zhagnsan01
zhagnsan02

【PooledJedisFactory】池化redis工厂

public class PooledJedisFactory {private JedisPool jedisPool;public static PooledJedisFactory build() {// 创建连接池配置GenericObjectPoolConfig<Jedis> poolConfig = new GenericObjectPoolConfig<>();// 设置连接池属性poolConfig.setMaxTotal(10); // 最大连接数poolConfig.setMaxIdle(5); // 最大空闲连接数poolConfig.setMinIdle(1); // 最小空闲连接数poolConfig.setJmxEnabled(true); // 开启jmxpoolConfig.setMaxWait(Duration.ofSeconds(3)); // 连接池没有连接后客户端的最大等待时间(单位毫秒)PooledJedisFactory busiJedisFactoryUsingPool =new PooledJedisFactory(poolConfig, "192.168.163.211", 6379);return busiJedisFactoryUsingPool;}private PooledJedisFactory(GenericObjectPoolConfig<Jedis> poolConfig, String host, int port) {jedisPool = new JedisPool(poolConfig, host, port);}public Jedis getJedis() {return jedisPool.getResource();}
}


【3.1.1】池化Jedis对象#close方法解析

1)Jedis#close方法源码

public void close() {if (this.dataSource != null) { // 表示使用的是连接池Pool<Jedis> pool = this.dataSource;this.dataSource = null;if (this.isBroken()) { // 判断当前连接是否已经断开pool.returnBrokenResource(this);} else {pool.returnResource(this); }} else { // 表示直连 //  直接关闭jedis连接 this.connection.close();}}

【代码解说】

  • dataSource != null: 表示使用的是连接池, 所以jedis.close() 方法表示归还连接给连接池,而jedis会判断当前连接是否已经断开;
  • dataSource=null :表示直连, jedis.close() 方法表示直接关闭jedis连接;


【3.2】Jedis连接池JedisPool配置属性概览

1)Jedis连接池配置使用 apache的通用对象池工具common-pool中的GenericObjectPoolConfig类;

在这里插入图片描述



【4】redis客户端常见异常总结(共计8个)

【4.1】问题1-无法从连接池获取jedis连接

1)无法从连接池获取jedis连接的原因:

  • 原因1-客户端: 高并发下连接池设置过小,供不应求;
  • 原因2-客户端:没有正确使用连接池,jedis连接没有释放;
  • 原因3-客户端:存在慢查询,慢查询会导致业务线程归还Jedis连接速度变慢,最终导致连接池被顶满;
  • 原因4-服务端:redis服务器执行客户端命令时存在阻塞,与慢查询类似,会导致业务线程归还Jedis连接速度变慢,最终导致连接池被顶满;


【4.1.1】模拟无法从连接池获取连接

1)业务场景:5个线程从包含3个连接的连接池获取连接;

【PooledJedisConnTimeoutMain】带有连接超时时间的池化Jedis测试案例

public class PooledJedisGetConnFailMain01 {public static void main(String[] args) {// 注意: 连接超时时间给定为2000毫秒PooledJedisWithConnSoTimeoutFactory pooledJedisFactory = PooledJedisWithConnSoTimeoutFactory.build(3, 2000);// 新建带有10个线程的线程池int threadCount = 5;ExecutorService executorService = Executors.newFixedThreadPool(threadCount);for (int i = 0; i < threadCount; i++) {final int index = i;executorService.execute((() -> pooledJedisFactory.getJedis(index)));}// 关闭连接池executorService.shutdown();}
}

【打印日志】

index=1, 耗时(秒)=0
index=3, 耗时(秒)=0
index=2, 耗时(秒)=0
index=4, 耗时(秒)=2 // 显然,我们设置的连接超时时间为2000毫秒=2秒  
index=0, 耗时(秒)=2 
Exception in thread "pool-1-thread-5" java.lang.RuntimeException: redis.clients.jedis.exceptions.JedisException: Could not get a resource from the poolat com.tom.redisdiscover.jedispool.timeout.PooledJedisWithConnSoTimeoutFactory.getJedis(PooledJedisWithConnSoTimeoutFactory.java:42)at com.tom.redisdiscover.jedispool.timeout.PooledJedisConnTimeoutMain.lambda$main$0(PooledJedisConnTimeoutMain.java:21)at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)at java.base/java.lang.Thread.run(Thread.java:842)
Caused by: redis.clients.jedis.exceptions.JedisException: Could not get a resource from the poolat redis.clients.jedis.util.Pool.getResource(Pool.java:42)at redis.clients.jedis.JedisPool.getResource(JedisPool.java:378)at com.tom.redisdiscover.jedispool.timeout.PooledJedisWithConnSoTimeoutFactory.getJedis(PooledJedisWithConnSoTimeoutFactory.java:40)... 4 more
Caused by: java.util.NoSuchElementException: Timeout waiting for idle object, borrowMaxWaitDuration=PT2S

【PooledJedisWithConnSoTimeoutFactory】

public class PooledJedisWithConnSoTimeoutFactory {private JedisPool jedisPool;public static PooledJedisWithConnSoTimeoutFactory build(int maxTotal, int maxWaitMillis) {// 创建连接池配置GenericObjectPoolConfig<Jedis> poolConfig = new GenericObjectPoolConfig<>();// 设置连接池属性poolConfig.setMaxTotal(maxTotal); // 最大连接数poolConfig.setMaxIdle(5); // 最大空闲连接数poolConfig.setMinIdle(1); // 最小空闲连接数poolConfig.setJmxEnabled(true); // 开启jmxpoolConfig.setMaxWait(Duration.ofMillis(maxWaitMillis)); // 连接池没有连接后客户端的最大等待时间(单位毫秒)poolConfig.setBlockWhenExhausted(true); // 当连接池用尽后,调用者是否等待;该参数为true时,maxWait才起作用return new PooledJedisWithConnSoTimeoutFactory(poolConfig, "192.168.163.211", 6379);}private PooledJedisWithConnSoTimeoutFactory(GenericObjectPoolConfig<Jedis> poolConfig, String host, int port) {jedisPool = new JedisPool(poolConfig, host, port);}public Jedis getJedis(int index) {long start = System.currentTimeMillis();try {return jedisPool.getResource();} catch (Exception e) {throw new RuntimeException(e);} finally {long costOfSecond = (System.currentTimeMillis() - start) / 1000;System.out.printf("index=%s, 耗时(秒)=%d\n", index, costOfSecond);}}
}

【补充】设置获取jedis连接超时时间的注意事项:

  • 当blockWhenExhausted=true时,maxWaitMillis才会生效,否则不会生效;setter方法分别是setBlockWhenExhausted, setMaxWait;
  • 当blockWhenExhausted=true时,而maxWaitMillis不设置,则默认maxWaitMillis为-1, -1表示永不超时; 这是有非常大的问题的;即redis连接不上,则应用启动一直阻塞;


【4.2】问题2-客户端读写超时

1)客户端读写超时原因:

  • 读写超时时间设置过短;
  • 命令本身执行慢;
  • 客户端与服务器网络不正常;
  • redis自身发生阻塞;


【4.2.1】模拟客户端读写超时场景

1)查询包含300w个元素的列表,超时时间设置为100ms,报Socket超时;

【JedisRwSocketTimeoutMain02】

public class JedisRwSocketTimeoutMain02 {public static void main(String[] args) {int connectTimeout = 3000;int soTimeout = 100;long start = System.currentTimeMillis();try {Jedis jedis = new Jedis("192.168.163.211", 6379, connectTimeout, soTimeout);List<String> charList01 = jedis.lrange("charList01", 0, -1);System.out.println(charList01.size());} catch (Exception e) {throw new RuntimeException(e);} finally {long costOfMilliSecond = System.currentTimeMillis() - start;System.out.printf("耗时(毫秒)=%d\n", costOfMilliSecond);}}
}

【日志】

耗时(毫秒)=211
Exception in thread "main" java.lang.RuntimeException: redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed outat com.tom.redisdiscover.jedispool.clientcommonexception.JedisRwSocketTimeoutMain02.main(JedisRwSocketTimeoutMain02.java:24)


【4.3】问题3-客户端连接超时

1)客户端连接超时原因:

  • 连接超时设置过短;
  • redis发生阻塞,导致 tcp-backlog 已满, 造成新的连接超时;
  • 客户端与服务器网络不正常;


【4.3.1】客户端连接超时场景

1)业务场景:连接到一个不存在的redis服务器;(192.168.163.222 机器上没有部署redis服务器)

public class JedisConnectionTimeoutMain03 {public static void main(String[] args) {int connectTimeout = 3000;int soTimeout = 2000;long start = System.currentTimeMillis();try {Jedis jedis = new Jedis("192.168.163.222", 6379, connectTimeout, soTimeout);} catch (Exception e) {throw new RuntimeException(e);} finally {long costOfSecond = (System.currentTimeMillis() - start) / 1000;System.out.printf("耗时(秒)=%d\n", costOfSecond);}}
}

【日志】

耗时(秒)=3
Exception in thread "main" java.lang.RuntimeException: redis.clients.jedis.exceptions.JedisConnectionException: Failed to connect to 192.168.163.222:6379.at com.tom.redisdiscover.jedispool.timeout.JedisConnectionTimeoutMain.main(JedisConnectionTimeoutMain.java:20)


【4.4】问题4-客户端缓冲区异常

1)客户端缓冲区异常原因:

  • 输出缓冲区满;
  • 长时间闲置连接被服务端主动断开;
  • 不正常并发读写:Jedis对象同时被多个线程并发操作,可能该异常;


【4.4.1】模拟客户端缓冲区异常场景

1)redis服务器的普通客户端的输出缓冲区设置为1k,最大2k,如下;

【 redis-6379.conf 】 redis服务器启动配置文件

port 6379
dir /redis/data
dbfilename "dump-6379.rdb"
bind 192.168.163.211
protected-mode no## normal client conf (redis服务器的普通客户端的输出缓冲区设置为1k,最大2k)
client-output-buffer-limit normal 2kb 1kb 1## slave node client conf
client-output-buffer-limit replica 256mb 64mb 60## pubsub client conf
client-output-buffer-limit pubsub 32mb 8mb 60

注意: 若client-output-buffer-limit normal 配置为0 0 0 ,则表示不限制;

client-output-buffer-limit normal 0 0 0

2)从redis读取包含300w个元素的有序列表,报输出缓冲区异常

【JedisClientBufferExceptionMain04】 客户端缓冲区异常测试案例

public class JedisClientBufferExceptionMain04 {public static void main(String[] args) {int connectTimeout = 3000;int soTimeout = 60000 * 3600;long start = System.currentTimeMillis();try {Jedis jedis = new Jedis("192.168.163.211", 6379, connectTimeout, soTimeout);for (int i = 0; i < 100000; i++) {List<String> charList01 = jedis.lrange("charList01", 0, -1);System.out.println(charList01.size());}} catch (Exception e) {throw new RuntimeException(e);} finally {long costOfMilliSecond = System.currentTimeMillis() - start;System.out.printf("耗时(毫秒)=%d\n", costOfMilliSecond);}}
}

【日志】 Unexpected end of stream 表示客户端数据流异常

耗时(毫秒)=270
Exception in thread "main" java.lang.RuntimeException: redis.clients.jedis.exceptions.JedisConnectionException: Unexpected end of stream.at com.tom.redisdiscover.jedispool.clientcommonexception.JedisRwSocketTimeoutMain02.main(JedisRwSocketTimeoutMain02.java:28)
Caused by: redis.clients.jedis.exceptions.JedisConnectionException: Unexpected end of stream.


【4.5】问题5-lua脚本正在执行(仅了解)



【4.6】redis正在加载持久化文件(仅了解)



【4.7】redis使用的内存超过maxmemory设置

1)原因:Jedis执行写操作时,如果redis的使用内存大于 maxmemory的设置,会报如下异常;

OOM command not allowed when used memory 'maxmemory'

【4.7.1】模拟redis使用的内存超过maxmemory设置

1)设置内存大小为10kb

【redis-6379.conf 】

# max memory conf
maxmemory 10kb

2)向redis写入26w个字符的value

【JedisClientOverMaxMemoryExceptionMain07】 redis使用的内存超过maxmemory设置测试案例

public class JedisClientOverMaxMemoryExceptionMain07 {private static final String VALUE = "abcdefghijklmnopqrstuvwxyz";public static void main(String[] args) {int connectTimeout = 3000;int soTimeout = 6000 * 3600;long start = System.currentTimeMillis();try {Jedis jedis = new Jedis("192.168.163.211", 6379, connectTimeout, soTimeout);int size = 10000;StringBuilder result = new StringBuilder();for (int i = 0; i < size; i++) {result.append(VALUE + i).append("#");}// 执行写操作jedis.set("bigKey", result.toString());} catch (Exception e) {throw new RuntimeException(e);} finally {long costOfMilliSecond = System.currentTimeMillis() - start;System.out.printf("耗时(毫秒)=%d\n", costOfMilliSecond);}}
}

【报错日志】

耗时(毫秒)=148
Exception in thread "main" java.lang.RuntimeException: redis.clients.jedis.exceptions.JedisDataException: OOM command not allowed when used memory > 'maxmemory'.at com.tom.redisdiscover.jedispool.clientcommonexception.JedisClientOverMaxMemoryExceptionMain07.main(JedisClientOverMaxMemoryExceptionMain07.java:32)
Caused by: redis.clients.jedis.exceptions.JedisDataException: OOM command not allowed when used memory > 'maxmemory'.


【4.8】客户端连接数过大

1)若客户端连接数超过maxclients, 新申请的连接报如下异常。

[root@centos211 ~]# redis-cli -h 192.168.163.211 -p 6379
192.168.163.211:6379> set name1 tom1
(error) ERR max number of clients reached

这类问题比较棘手就, 因为无法执行redis命令修复问题。

2)解决方法:

  • 方法1-客户端问题:若maxclient参数比较大,通常是由于应用服务对于redis客户端使用不当造成的。如应用服务是分布式架构,每个服务内部使用连接池操作redis,每个连接池的最大连接为10,若100个实例,则最大连接数为1000个; 【下线部分服务节点,把连接数降下来】
  • 方法2-服务器问题:若客户端无法处理,而当前redis集群是哨兵或Cluster模式,可以考虑将当前redis做故障转移;


【4.8.1】模拟客户端连接数过大异常

1)设置最大连接数

# max clients conf
maxclients 5

2)打开5个linux客户端连接到redis,没有问题;

3)打开第6个linux客户端连接到redis,报错如下。

[root@centos211 ~]# redis-cli -h 192.168.163.211 -p 6379
192.168.163.211:6379> set name1 tom1
(error) ERR max number of clients reached

【JedisClientConnectionOverMaxClientsExceptionMain08】 jedis客户端连接到redis集群

public class JedisClientConnectionOverMaxClientsExceptionMain08 {public static void main(String[] args) {new Jedis("192.168.163.211", 6379, 3000, 1000);}
}

报错如下:

Exception in thread "main" redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketException: 你的主机中的软件中止了一个已建立的连接。at redis.clients.jedis.util.RedisInputStream.ensureFill(RedisInputStream.java:262)at redis.clients.jedis.util.RedisInputStream.readByte(RedisInputStream.java:55)


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/497468.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

KaiOS 4.0 | DataCall and setupData implemention

相关文档 1、KaiOS 3.1 系统介绍 KaiOS 系统框架和应用结构(APP界面逻辑)文章浏览阅读842次,点赞17次,收藏5次。对于Java开发者而言,理解JS的逻辑调用是有点困难的。而KaiOS webapp开发又不同于现代的web开发,更像chrome浏览器内嵌模式。在这里梳理一下kaios平台web应用…

正则化强度的倒数C——让模型学习更准确

引言 嘿&#xff0c;小朋友们&#xff0c;今天我们要学习一个叫做正则化强度倒数C的概念。这听起来可能有点复杂&#xff0c;但它其实是一种帮助计算机学习的方法。想象一下&#xff0c;我们教计算机识别动物&#xff0c;我们希望它既能识别出猫&#xff0c;也能识别出狗&…

飞牛NAS登录Fn Connect教程实测|远程访问教程

前言 有很多小伙伴已经用上飞牛NAS系统很久了&#xff0c;但大部分都是在局域网下使用&#xff0c;如果外出办公或者在办公室&#xff0c;就没办法连接到家里的NAS。 于是咱们今天先出一条关于远程连接到飞牛NAS的教程。 关于飞牛NAS的教程会一步步发布&#xff0c;请耐心等…

Python编程快速上手:让繁琐工作自动化(第2版)下载

适读人群 &#xff1a;本书适合任何想要通过Python学习编程的读者&#xff0c;尤其适合缺乏编程基础的初学者。通过阅读本书&#xff0c;读者将能利用非常强大的编程语言和工具&#xff0c;并且体会到用Python编程的快乐。 Python编程从入门到实践姊妹篇&#xff0c;零基础自学…

短视频矩阵账号管理技术源码搭建详解,支持OEM

一、引言 在短视频矩阵系统中&#xff0c;账号管理是至关重要的一环&#xff0c;它涉及到多平台账号的接入、用户信息的安全存储与高效管理、权限的精准控制以及账号数据的同步与更新等关键功能。一个健壮、灵活且安全的账号管理技术架构&#xff0c;能够为整个短视频矩阵系统的…

精读DeepSeek v3技术文档的心得感悟

最近宋大宝同学读完了DeepSeekv3的文档&#xff0c;心中颇多感慨&#xff0c;忍不住想在这里记录一下对这款“业界有望启示未来低精度训练走向”的开源大模型的观察与思考。DeepSeek v3的亮点绝不仅仅是“Float8”或“超长上下文”这么简单&#xff0c;而是贯穿了从数值精度、注…

43243242342

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &#x1f4e2;本文由 JohnKi 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f4e2;未来很长&#…

Spring Boot教程之四十:使用 Jasypt 加密 Spring Boot 项目中的密码

如何使用 Jasypt 加密 Spring Boot 项目中的密码 在本文中&#xff0c;我们将学习如何加密 Spring Boot 应用程序配置文件&#xff08;如 application.properties 或 application.yml&#xff09;中的数据。在这些文件中&#xff0c;我们可以加密用户名、密码等。 您经常会遇到…

2011-2019年各省总抚养比数据

2011-2019年各省总抚养比数据 1、时间&#xff1a;2011-2019年 2、来源&#xff1a;国家统计局 3、指标&#xff1a;行政区划代码、地区、年份、总抚养比(人口抽样调查)(%) 4、范围&#xff1a;31省 5、指标解释&#xff1a;总抚养比也称总负担系数。指人口总体中非劳动年…

Java基于SpringBoot的社区团购系统的设计与实现,附源码

博主介绍&#xff1a;✌Java老徐、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&…

libreoffice在Windows和Linux环境的安装和结合Springboot使用教程

前言&#xff1a; 在公司做开发时&#xff0c;遇到一个需求&#xff0c;要求上传的文件有图片&#xff0c;也有word和pdf。预览信息时&#xff0c;既要求能水印展示出来&#xff0c;又要求能大图水印预览。思索许久&#xff0c;我决定采取全部打水印然后转成图片Base64&#x…

Linux实验报告7-文件管理

目录 一&#xff1a;实验目的 二&#xff1a;实验内容 (1)查看/etc/inittab文件的权限属性&#xff0c;并指出该文件的所有者以及文件所属组群。 (2)新建文件test&#xff0c;设置文件权限为r--r-----。 (3)新建文件test2&#xff0c;设系统中有用户study和用户组studygr…

07 基于OpenAMP的核间通信方案

引言 ZYNQ7020有两个CPU核心&#xff0c;这两个核心可以采用SMP或AMP方式进行调度&#xff0c;当采用AMP方式进行调度时核0和核1可以运行不同的操作系统&#xff0c;如核0运行Linux系统&#xff0c;提供有些复杂的用户交互工作&#xff0c;核1运行实时操作系统&#xff0c;对设…

Huggingface数据集采样之后得到的arrow文件无法用Dataset.load_from_disk加载解决方法

问题起源 我现在有数据集 我想要采样1/10构成一个新的数据集。起初我只是简单的使用save_to_disk保存&#xff0c;如下代码&#xff1a; from datasets import Dataset import os# 原数据集路径 source_dataset_path "~/.cache/huggingface/datasets/allenai___tulu-3-…

【畅购商城】购物车模块之查看购物车

目录 分析 接口 后端实现 前端实现&#xff1a;显示页面 前端实现&#xff1a;显示购物车信息 分析 用户如果没有登录&#xff0c;购物车存放在浏览器端的localStorage处&#xff0c;且以数组的方式进行存储。用户如果登录了&#xff0c;购物车存放在redis中&#xff0c…

html+css+js网页设计 美食 逛吃网7个页面

htmlcssjs网页设计 美食 逛吃网7个页面 网页作品代码简单&#xff0c;可使用任意HTML辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xff09;。 该网站是美食菜谱社区…

记录C#知识点(三)41-60

目录 41.winform启动wpf程序 42.winform调用wpf 43.nuget安装包问题 41.winform启动wpf程序 winform启动wpf程序的时候 以下方式会导致winform界面有问题 WpfApp1.App.Main(); 使用下面的方式 可以解决 winform启动 new WindowsFormsApp1.Form1().Show(); wpf启动 new …

图解Modern Cpp内存序

文章目录 为什么需要内存序?内存序的分类内存序的作用指令重排序限制跨线程可见性 样例分析完全乱序: memory_order_relaxed读写同步: memory_order_acquire 和 memory_order_release一致性 memory_order_seq_cst6. 内存序的权衡总结 C 内存序(Memory Order)定义了多线程环境下…

MySQL第二弹----CRUD

笔上得来终觉浅,绝知此事要躬行 &#x1f525; 个人主页&#xff1a;星云爱编程 &#x1f525; 所属专栏&#xff1a;MySQL &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 ​ 一、修改表 使用ALTER …

Pytorch | 利用SMI-FGRM针对CIFAR10上的ResNet分类器进行对抗攻击

Pytorch | 利用I-FGSSM针对CIFAR10上的ResNet分类器进行对抗攻击 CIFAR数据集SMI-FGRM介绍SMI-FGRM算法流程 SMI-FGRM代码实现SMI-FGRM算法实现攻击效果 代码汇总smifgrm.pytrain.pyadvtest.py 之前已经针对CIFAR10训练了多种分类器&#xff1a; Pytorch | 从零构建AlexNet对CI…