Redis: java客户端

文章目录

  • 一、Redis的Java客户端
    • 1、Jedis
      • (1)Jedis操作Redis
      • (2)Jedis连接池
    • 2、lettuce
    • 3、Redisson
    • 4、SpringDataRedis客户端
      • (1)介绍
      • (2)序列化
      • (3)StringRedisTemplate
  • 二、jedis连接工具类

一、Redis的Java客户端

java客户端官网:https://redis.io/resources/clients/#java

1、Jedis

以Redis命令作为方法名称,学习成本低,简单使用。但是Jedis实例是线程不安全的,多线程环境下需要基于连接池来使用。

(1)Jedis操作Redis

官网地址:https://github.com/redis/jedis
Jedis使用的基本步骤:

  • 引入依赖。
  • 创建Jedis对象,建立连接。
  • 使用Jedis,方法名与Redis命令一致。
  • 释放资源。

(2)Jedis连接池

Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此推荐大家使用Jedis连接池代替Jedis的直连方式。 在这里插入图片描述
Jedis非线程安全问题可以参考文章:Jedis非线程安全问题 ,该文章总结了Jedis非线程安全的原因:

  • 共享socket引起的异常。
  • 共享数据流引起的异常。

2、lettuce

Lettuce是基于Netty实现的,支持同步、异步和响应式编程方式,并且线程安全的。支持Redis的哨兵模式、集群模式和管道模式。

3、Redisson

Redisson是一个基于Redis实现的分布式、可伸缩的Java数据结构集合。包含了诸如Map、Queue、Lock、Semaphore、AtomicLong等强大功能。

4、SpringDataRedis客户端

(1)介绍

SpringData是Spring中数据操作的模块,包含了对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis,官网地址:https://spring.io/projects/spring-data-redis:

  • 提供了对不同Redis客户端的整合(Lettuce和Jedis)。
  • 提供了RedisTemplate统一API来操作Redis。
  • 支持Redis的发布订阅模型。
  • 支持Redis哨兵和Redis集群。
  • 支持基于Lettuce的响应式编程。
  • 支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化。
  • 支持基于Redis的JDKCollection实现。
    在这里插入图片描述

SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。并且将不同数据类型的操作API封装到了不同的类型中:

(2)序列化

  • JDK
    RedisTemplate可以接收任意Object作为值写入redis,只不过写入前会把Object序列化为字节形式,默认是采用JDK序列化,形式:
    在这里插入图片描述

    • 缺点
      • 可读性差
      • 内存占用较大
    • 序列化对象需要实现Serializable接口
  • JSON
    这里采用了JSON序列化来代替默认的JDK序列化方式,结果:
    在这里插入图片描述
    整体可读性有了很大提升,并且能将Java对象自动的序列化为JSON字符串,并且查询时能自动把JSON反序列化为Java对象。不过,其中记录了序列化时对应的class名称,目的是为了查询时实现自动反序列化。这会带来额外的内存开销。

(3)StringRedisTemplate

为了节省内存空间,我们可以不使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value。当需要存储Java对象时,手动完成对象的序列化和反序列化。因为存入和读取时的序列化及反序列化都是我们自己实现的,SpringDataRedis就不会将class信息写入Redis了。这种用法比较普遍,因此SpringDataRedis就提供了RedisTemplate的子类:StringRedisTemplate,它的key和value的序列化方式默认就是String方式。
在这里插入图片描述

二、jedis连接工具类

import cn.hutool.core.lang.Assert;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.util.CollectionUtils;
import redis.clients.jedis.*;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
@Slf4j
public class RedisOperateUtil {

    private final static Map<String, ConnectStrategy> CONNECT_CACHE = new HashMap<>(3);
    private final static AtomicBoolean INIT_STATUS = new AtomicBoolean(Boolean.FALSE);
    private final static String PONG = "PONG";

    public static ConnectStrategy getConnectStrategy(String connectType) {
        if (!INIT_STATUS.getAndSet(Boolean.TRUE)) {
            log.info("线程: {}, 初始化redis连接测试策略", Thread.currentThread().getName());
            MasterSlaveConnect.getInstance();
            ClusterConnect.getInstance();
            SentinelConnect.getInstance();
            SingleConnect.getInstance();
        }
        return Optional.ofNullable(CONNECT_CACHE.get(connectType)).orElseThrow(() -> new RuntimeException("该连接模式未适配或并未查询到该集群类型!"));
    }

    public interface ConnectStrategy {
        /**
         * 连接测试方法
         */
        void connectTest(Map<String, String> parameters);
    }

    static abstract class AbstractConnectStrategy {
        public void register(String name, ConnectStrategy strategy) {
            CONNECT_CACHE.putIfAbsent(name, strategy);
        }
    }

    private static class MasterSlaveConnect extends AbstractConnectStrategy implements ConnectStrategy {
        private static MasterSlaveConnect instance = new MasterSlaveConnect();
        private final static String connectName = "masterSlave";

        public static MasterSlaveConnect getInstance() {
            return instance;
        }

        private MasterSlaveConnect() {
            register(connectName, this);
        }

        @Override
        public void connectTest(Map<String, String> parameters) {
            String masterNode = parameters.get(RedisEnum.MASTER_NODE.getKey());
            String password = parameters.get(RedisEnum.PASSWORD.getKey());
            String[] split = masterNode.split(StrUtil.COLON);
            if(split.length != 2){
                throw new RuntimeException("主节点格式不对,只从直接只能有一个主节点地址!");
            }
            commonTestConn(split,password);
        }

        private void commonTestConn(String[] split,String passWord) {
            Jedis mnode = null;
            if(!StringUtils.isEmpty(passWord)){
                JedisPoolConfig jedisPoolConfig = jedisPoolConfig();

                try(JedisPool jedisPool = new JedisPool(jedisPoolConfig, split[0].trim(), Integer.valueOf(split[1].trim()), 3000,passWord)) {
                    mnode = jedisPool.getResource();
                } catch (Exception e) {
                    throw new RuntimeException("redis连接失败!", e);
                }
            }else{
                mnode = new Jedis(split[0].trim(),Integer.valueOf(split[1].trim()),3000);
            }
            try {
                String ping = mnode.ping();
                Assert.isTrue(PONG.equals(ping), "连通性测试失败");
            } catch (Exception e) {
                throw new RuntimeException("连通性测试失败!", e);
            } finally {
                mnode.close();
            }
        }

        private static JedisPoolConfig jedisPoolConfig(){
            // Jedis连接池配置
            JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
            // 最大空闲连接数, 默认8个
            jedisPoolConfig.setMaxIdle(100);
            // 最大连接数, 默认8个
            jedisPoolConfig.setMaxTotal(500);
            //最小空闲连接数, 默认0
            jedisPoolConfig.setMinIdle(0);
            // 获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间,  默认-1
            // 设置2秒
            jedisPoolConfig.setMaxWaitMillis(3000);
            //对拿到的connection进行validateObject校验
            jedisPoolConfig.setTestOnBorrow(true);
            return jedisPoolConfig;
        }
    }

    private static class ClusterConnect extends AbstractConnectStrategy implements ConnectStrategy{

        private static ClusterConnect instance = new ClusterConnect();
        private final static String connectName = "cluster";

        public static ClusterConnect getInstance() {
            return instance;
        }

        private ClusterConnect () {
            register(connectName, this);
        }

        @Override
        public void connectTest(Map<String, String> parameters) {
            String clusterNodes = parameters.get(RedisEnum.CLUSTER_NODES.getKey());
            String password = parameters.get(RedisEnum.PASSWORD.getKey());
            String[] serverArray = clusterNodes.split(StrUtil.COMMA);
            Set<HostAndPort> nodes = new LinkedHashSet<HostAndPort>();
            for (String array : serverArray) {
                String[] strings = array.split(StrUtil.COLON);
                HostAndPort hostAndPort = new HostAndPort(strings[0],Integer.valueOf(strings[1]));
                nodes.add(hostAndPort);
            }
            JedisCluster jedisCluster=null;
            try {
                if(StringUtils.isNotEmpty(password)){
                    GenericObjectPoolConfig gopc = new GenericObjectPoolConfig();
                    gopc.setMaxTotal(32);
                    gopc.setMaxIdle(4);
                    gopc.setMaxWaitMillis(6000);
                    jedisCluster = new JedisCluster(nodes,3000,3000,3,password,gopc);
                    jedisCluster.exists("tt");
                }else {
                    jedisCluster = new JedisCluster(nodes,3000);
                    jedisCluster.exists("tt");
                }
            } catch (Exception e) {
                throw new RuntimeException("连通性测试失败!",e);
            } finally {
                if (jedisCluster != null) {
                    try {
                        jedisCluster.close();
                    } catch (IOException e) {
                       log.error(e.getMessage(), e);
                    }
                }
            }
        }
    }

    private static class SentinelConnect extends AbstractConnectStrategy implements ConnectStrategy {
        private static SentinelConnect instance = new SentinelConnect();
        private final static String connectName = "sentinel";

        public static SentinelConnect getInstance() {
            return instance;
        }

        private SentinelConnect () {
            register(connectName, this);
        }

        @Override
        public void connectTest(Map<String, String> parameters) {
            log.info("redis connect test parameters: {}", parameters);
            String sentinelNodes = Optional.ofNullable(parameters.get(RedisEnum.SENTINEL_NODES.getKey()).trim())
                    .orElseThrow(() -> new RuntimeException("sentinelNodes is null"));
            String masterName = Optional.ofNullable(parameters.get(RedisEnum.MASTER_NAME.getKey()).trim())
                    .orElseThrow(() -> new RuntimeException("masterName is null"));
            String[] serverArray = sentinelNodes.split(StrUtil.COMMA);
            String password = parameters.get(RedisEnum.PASSWORD.getKey());
            Set<String> sentinels = new HashSet<>(Arrays.asList(serverArray));
            log.info("sentinels: {}, masterName: {}, password: {}", sentinels, masterName, parameters);
            JedisSentinelPool jedisSentinelPool = null;
            if (StringUtils.isNotBlank(password)) {
                jedisSentinelPool = new JedisSentinelPool(masterName, sentinels, password);
            } else {
                jedisSentinelPool = new JedisSentinelPool(masterName, sentinels);
            }
            HostAndPort currentHostMaster = jedisSentinelPool.getCurrentHostMaster();
            Jedis jedis = jedisSentinelPool.getResource();
            try {
                String ping = jedis.ping();
                Assert.isTrue(PONG.equals(ping), "连通性测试失败");
            } catch (Exception e) {
                throw new RuntimeException(currentHostMaster.toString() + " : 连通测试失败!");
            } finally {
                jedis.close();
                jedisSentinelPool.close();
            }
        }
    }

    private static class SingleConnect extends AbstractConnectStrategy implements ConnectStrategy {
        private static SingleConnect instance = new SingleConnect();
        private final static String connectName = "single";

        public static SingleConnect getInstance() {
            return instance;
        }

        private SingleConnect() {
            register(connectName, this);
        }

        @Override
        public void connectTest(Map<String, String> parameters) {
            String singleNode = parameters.get(RedisEnum.SINGLE_NODE.getKey());
            Assert.notBlank(singleNode, "singleNode is null!");
            String password = parameters.get(RedisEnum.PASSWORD.getKey());
            Assert.notBlank(password, "password is null");
            String[] split = singleNode.split(StrUtil.COLON);
            Jedis jedis = new Jedis(split[0], Integer.valueOf(split[1]));
            if (StringUtils.isNotBlank(password)) {
                jedis.auth(password);
            }
            String ping = jedis.ping();
            Assert.isTrue(PONG.equals(ping), "连通性测试失败");
        }
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

中国人工智能产业年会智能交通与自动驾驶专题全景扫描

中国人工智能产业年会&#xff08;CAIIAC&#xff09;是中国人工智能技术发展和应用的重要展示平台&#xff0c;不仅关注创新&#xff0c;还涵盖了市场和监管方面的内容&#xff0c;对于促进人工智能领域的发展起到了重要作用。年会汇集了来自学术界、工业界和政府的专家&#…

Python数据可视化:无向网络图

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 Python数据可视化&#xff1a; 无向网络图 [太阳]选择题 关于以下代码输出结果的说法中正确的是? import networkx as nx import matplotlib.pyplot as plt a [(A, B), (B, C), (B, D)] …

基于小程序实现的4s店管理系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 技术选型 【后端】&#xff1a;Java 【框架】&#xff1a;ssm 【…

字体反爬积累知识

目录 一、什么是字体反扒 二、Unicode编码 三、利用font包获取映射关系 一、什么是字体反扒 字体反爬是一种常见的反爬虫技术&#xff0c;它通过将网页中的文本内容转换为特殊的字体格式来防止爬虫程序直接获取和解析文本信息。字体反爬的原理是将常规的字符映射到特殊的字…

MyBatisPlus自定义SQL

✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉🍎个人主页:Leo的博客💞当前专栏: 循序渐进学SpringBoot ✨特色专栏: MySQL学习 🥭本文内容:MyBatisPlus自定义SQL 📚个人知识库: Leo知识库,欢迎大家访问 目录 1.前言☕…

ArcGIS三维景观分层显示

今天将向大家介绍的事在ArcGIS中如何创建多层三维显示。 地表为影像的 地表为地形晕渲的 在土壤分层、油气分层等都有着十分重要的应用。下面我们具体来看看实现过程 一、 准备数据及提取栅格范围 我们这次准备的数据是之前GIS100例-30讲的案例数据。《ArcGIS三维影像图剖面图…

WebRTC直播间搭建记录

考虑到后续增加平台直播的可能性&#xff0c;笔记记录一下WebRTC相关. 让我们分别分析两种情况下的WebRTC连接建立过程&#xff1a; 情况一&#xff1a;AB之间可以直接通信 1.信令交换&#xff1a; 设备A和设备B首先通过信令服务器交换SDP&#xff08;Session Description Pr…

科技驱动未来,提升AI算力,GPU扩展正当时

要说这两年最火的科技是什么&#xff1f;我想“AI人工智能”肯定是最有资格上榜的&#xff0c;尤其ChatGPT推出后迅速在社交媒体上走红&#xff0c;短短5天&#xff0c;注册用户数就超过100万&#xff0c;2023年一月末&#xff0c;ChatGPT的月活用户更是突破1亿&#xff0c;成为…

嵌入式4-16

tftpd #include <myhead.h> #define SER_IP "192.168.125.243" //服务器IP地址 #define SER_PORT 69 //服务器端口号 #define CLI_IP "192.168.125.244" //客户端IP地址 #define CLI_PORT 8889 //客户端端…

C#创建磁性窗体的方法:创建特殊窗体

目录 一、磁性窗体 二、磁性窗体的实现方法 (1)无标题窗体的移动 (2)Left属性 (3)Top属性 二、设计一个磁性窗体的实例 &#xff08;1&#xff09;资源管理器Resources.Designer.cs设计 &#xff08;2&#xff09;公共类Frm_Play.cs &#xff08;3&#xff09;主窗体 …

Java Spring 框架下利用 MyBatis 实现请求 MySQL 数据库的存储过程

Java Spring 框架下利用 MyBatis 实现请求 MySQL 数据库的存储过程 环境准备与前置知识1. 创建 MySQL 存储过程2. 配置数据源3. 创建实体类4. 创建 Mapper 接口5. 创建 Mapper XML 文件6. 创建 Service 接口及Impl实现类7. 创建 Controller 类8. 测试与总结 在现代的 Web 应用开…

STM32 F103 C8T6开发笔记14:与HLK-LD303-24G测距雷达通信

今日尝试配通STM32 F103 ZET6与HLK-LD303-24G测距雷达的串口通信解码 文章提供测试代码...... 目录 HLK-LD303-24G测距雷达外观&#xff1a; 线路连接准备&#xff1a; 定时器与串口配置准备&#xff1a; 定时器2的初始化&#xff1a; 串口1、2初始化&#xff1a; 串口1、2自定…

ARP代理

10.1.0.1/8 和10.2.0.1/8是在同一个网段 10.1.0.2/16 和10.2.0.2/16 不在同一个网段 10.1.0.1/8 和10.1.0.2/16 是可以ping通的 包发出来了&#xff0c;报文有发出来&#xff0c;目的地址是广播包 广播请求&#xff0c;发到路由器的接口G 0/0/0 target不是本接口&#xff0…

【C++学习】C++IO流

这里写目录标题 &#x1f680;C语言的输入与输出&#x1f680;什么是流&#x1f680;CIO流&#x1f680;C标准IO流&#x1f680;C文件IO流 &#x1f680;C语言的输入与输出 C语言中我们用到的最频繁的输入输出方式就是scanf ()与printf()。 scanf(): 从标准输入设备(键盘)读取…

windows网络驱动开发

基石&#xff1a;WFP 1、简介 Windows过滤平台&#xff08;Windows Filtering Platform, WFP&#xff09;&#xff0c;是从Vista系统后新增的一套系统API和服务。开发者可以在WFP框架已划分的不同分层中进行过滤、重定向、修改网络数据包&#xff0c;以实现防火墙、入侵检测系…

pdf做批注编辑工具 最新pdf reader pro3.3.1.0激活版

PDF Reader Pro是一款功能强大的PDF阅读和编辑工具。它提供了多种工具和功能&#xff0c;帮助用户对PDF文档进行浏览、注释、编辑、转换和签名等操作。以下是PDF Reader Pro的一些主要特色&#xff1a; 最新pdf reader pro3.3.1.0激活版下载 多种查看模式&#xff1a;PDF Reade…

上海计算机学会 2023年10月月赛 乙组T4 树的覆盖(树、最小点覆盖、树形dp)

第四题&#xff1a;T4树的覆盖 标签&#xff1a;树、最小点覆盖、树形 d p dp dp题意&#xff1a;求树的最小点覆盖集的大小和对应的数量&#xff0c;数量对 1 , 000 , 000 , 007 1,000,000,007 1,000,000,007取余数。所谓覆盖集&#xff0c;是该树的点构成的集合&#xff0c;…

vue:如何通过两个点的经纬度进行距离的计算(很简单)

首先假设从api获取到了自己的纬经度和别人的纬经度 首先有一个概念需要说一下 地球半径 由于地球不是一个完美的球体&#xff0c;所以并不能用一个特别准确的值来表示地球的实际半径&#xff0c;不过由于地球的形状很接近球体&#xff0c;用[6357km] 到 [6378km]的范围值可以…

板式热交换器强度

1、不同标准中对于板换压板的规定 (1) NB/T 47004.1-2017《板式热交换器 第1部分&#xff1a;可拆卸板式热交换器》6.3压紧板6.3.3条“压紧板应有足够的刚性&#xff0c;以保证板式热交换器在正常操作状态不发生泄漏”。 (2) NB/T 47004-2009《板式热交换器》5.3紧板5.3.3条“…

Springboot+Vue项目-基于Java+MySQL的蜗牛兼职网系统(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &…