`
QING____
  • 浏览: 2251571 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Redis客户端简单封装(spring)

 
阅读更多

    Jedis客户端已经比较易用了,不过在spring环境下,可能还需要简单的封装一下。

一、单节点Redis服务使用

    即只有一个Redis server,或者在M-S模式下,只有Master提供读写服务时使用,配置中指定Master的IP和Port即可。此处使用了Spring FactoryBean方式来创建JedisPool实例。

    1、SingletonClientFactoryBean.java

public class SingletonClientFactoryBean implements FactoryBean<JedisPool>,InitializingBean {

    private JedisPool jedisPool;

    private int maxTotal = 128;

    //最大空闲连接数
    private int maxIdle = 2;

    //最小空闲连接数
    private int minIdle = 1;
    //如果连接池耗尽,最大阻塞的时间,默认为6秒
    private long maxWait = 6000;//单位毫秒
    private String host;
    private int port;
    private int database = 0;//选择数据库,默认为0
    private int timeout = 3000;//connectionTimeout,soTimeout,默认为30秒

    private boolean testOnBorrow = true;
    private boolean testOnReturn = true;

    private String password;

    public void setMaxTotal(int maxTotal) {
        this.maxTotal = maxTotal;
    }

    public void setMaxIdle(int maxIdle) {
        this.maxIdle = maxIdle;
    }

    public void setMinIdle(int minIdle) {
        this.minIdle = minIdle;
    }

    public void setMaxWait(long maxWait) {
        this.maxWait = maxWait;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public void setDatabase(int database) {
        this.database = database;
    }

    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    public void setTestOnBorrow(boolean testOnBorrow) {
        this.testOnBorrow = testOnBorrow;
    }

    public void setTestOnReturn(boolean testOnReturn) {
        this.testOnReturn = testOnReturn;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    protected JedisPoolConfig buildConfig() {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMinIdle(minIdle);
        config.setMaxIdle(maxIdle);
        config.setMaxTotal(maxTotal);
        config.setTestOnBorrow(testOnBorrow);
        config.setTestOnReturn(testOnReturn);
        config.setBlockWhenExhausted(true);
        config.setMaxWaitMillis(maxWait);
        config.setFairness(false);

        return config;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        JedisPoolConfig config = buildConfig();
        jedisPool = new JedisPool(config,host,port,timeout, password, database,null);
    }

    @Override
    public JedisPool getObject() throws Exception {
        return jedisPool;
    }

    @Override
    public Class<?> getObjectType() {
        return JedisPool.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

 

    2、Spring.xml配置

    <bean id="redisClient" class="com.demo.redis.spring.SingletonClientFactoryBean">
        <property name="host" value="127.0.0.1"/>
        <property name="port" value="6379" />
        <property name="maxTotal" value="256"/>
        <property name="maxIdle" value="8" />
        <property name="maxWait" value="3000" />
        <property name="timeout" value="3000" />
        <property name="minIdle" value="2" />
    </bean>

 

    3、客户端使用

        Jedis jedis = redisClient.getResource();
        String cacheContent = null;
        try {
            cacheContent = jedis.get("hello_world");
        }finally {
            redisClient.close();
        }
        //获取redis数据之后,立即释放连接,然后开始进行业务处理
        if(cacheContent == null) {
            //DB operation
        }
        //..

 

 二、基于M-S模式下读写分离

    通常情况下,Slave只是作为数据备份,不提供read操作,这种考虑是为了避免slave提供stale数据而导致一些问题。不过在很多场景下,即使slave数据有一定的延迟,我们仍然可以兼容或者正常处理,此时我们可以将slave提供read服务,并在M-S集群中将read操作分流,此时我们的Redis集群将可以支撑更高的QPS。

    本文实例中,仅仅提供了“读写分离”的样板,尚未对所有的redis方法进行重写和封装,请开发者后续继续补充即可。此外,slave节点如果异常,我们应该支持failover,这一部分特性后续在扩展。

    1、ReadWriteRedisClient.java

public class ReadWriteRedisClient implements InitializingBean {

    //master:port,slave:port,slave:port...
    //master first
    private String hosts;
    private JedisPool master;
    private List<JedisPool> slaves = new ArrayList<>();

    private int maxTotal = 128;

    //最大空闲连接数
    private int maxIdle = 2;

    //最小空闲连接数
    private int minIdle = 1;
    //如果连接池耗尽,最大阻塞的时间,默认为6秒
    private long maxWait = 6000;//单位毫秒
    private int database = 0;//选择数据库,默认为0
    private int timeout = 3000;//connectionTimeout,soTimeout,默认为30秒

    private boolean testOnBorrow = true;
    private boolean testOnReturn = true;

    private String password;

    private Random random = new Random();

    public void setMaxTotal(int maxTotal) {
        this.maxTotal = maxTotal;
    }

    public void setMaxIdle(int maxIdle) {
        this.maxIdle = maxIdle;
    }

    public void setMinIdle(int minIdle) {
        this.minIdle = minIdle;
    }

    public void setMaxWait(long maxWait) {
        this.maxWait = maxWait;
    }

    public void setDatabase(int database) {
        this.database = database;
    }

    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    public void setTestOnBorrow(boolean testOnBorrow) {
        this.testOnBorrow = testOnBorrow;
    }

    public void setTestOnReturn(boolean testOnReturn) {
        this.testOnReturn = testOnReturn;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setHosts(String hosts) {
        this.hosts = hosts;
    }

    protected JedisPoolConfig buildConfig() {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMinIdle(minIdle);
        config.setMaxIdle(maxIdle);
        config.setMaxTotal(maxTotal);
        config.setTestOnBorrow(testOnBorrow);
        config.setTestOnReturn(testOnReturn);
        config.setBlockWhenExhausted(true);
        config.setMaxWaitMillis(maxWait);
        config.setFairness(false);

        return config;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        JedisPoolConfig config = buildConfig();
        String[] hostAndPorts = hosts.split(",");
        String masterHP = hostAndPorts[0];
        String[] ms = masterHP.split(":");
        master = new JedisPool(config,ms[0],Integer.valueOf(ms[1]),timeout, password, database,null);
        if(hostAndPorts.length > 1) {
            for(int i = 1; i < hostAndPorts.length; i++) {
                String[] ss = hostAndPorts[i].split(":");
                JedisPool slave = new JedisPool(config,ss[0],Integer.valueOf(ss[1]),timeout, password, database,null);
                slaves.add(slave);
            }
        }
        slaves.add(master);
    }

    public String get(String key) {
        Jedis jedis = fetchResource(true);
        try {
            return jedis.get(key);
        } finally {
            jedis.close();
        }
    }

    public List<String> mget(String... keys) {
        Jedis jedis = fetchResource(true);
        try {
            return jedis.mget(keys);
        } finally {
            jedis.close();
        }
    }

    public String setex(String key,int seconds,String value) {
        Jedis jedis = fetchResource(false);
        try {
            return jedis.setex(key,seconds,value);
        } finally {
            jedis.close();
        }
    }

    public Long setnx(String key,String value) {
        Jedis jedis = fetchResource(false);
        try {
            return jedis.setnx(key,value);
        } finally {
            jedis.close();
        }
    }

    public String set(String key,String value) {
        Jedis jedis = fetchResource(false);
        try {
            return jedis.set(key,value);
        } finally {
            jedis.close();
        }
    }

    public Long del(String key) {
        Jedis jedis = fetchResource(false);
        try {
            return jedis.del(key);
        } finally {
            jedis.close();
        }
    }

    public Long expire(String key,int seconds) {
        Jedis jedis = fetchResource(false);
        try {
            return jedis.expire(key,seconds);
        } finally {
            jedis.close();
        }
    }

    public Boolean exists(String key) {
        Jedis jedis = fetchResource(false);
        try {
            return jedis.exists(key);
        } finally {
            jedis.close();
        }
    }

    public Long exists(String... keys) {
        Jedis jedis = fetchResource(false);
        try {
            return jedis.exists(keys);
        } finally {
            jedis.close();
        }
    }

    private Jedis fetchResource(boolean read) {
        if(slaves.isEmpty() || !read) {
            return master.getResource();
        }
        int size = slaves.size();
        int i = random.nextInt(size);
        return slaves.get(i).getResource();
    }


    public static void main(String[] args) throws Exception {
        String prefix = "_test_";
        ReadWriteRedisClient client = new ReadWriteRedisClient();
        client.setHosts("127.0.0.1:6379,127.0.0.1:6379");

        client.afterPropertiesSet();

        client.set(prefix + "10001","test");
        System.out.println(client.get(prefix + "10001"));
    }
}

 

    2、Spring.xml配置

    <bean id="readWriteRedisClient" class="com.demo.redis.spring.ReadWriteRedisClient">
        <property name="hosts" value="127.0.0.1:6379,127.0.0.1:7379"/>
        <property name="maxTotal" value="256"/>
        <property name="maxIdle" value="8" />
        <property name="maxWait" value="3000" />
        <property name="timeout" value="3000" />
        <property name="minIdle" value="2" />
    </bean>

 

    3、客户端使用

    @Autowired
    private JedisPool redisClient;
    
    public void test() {
        Jedis jedis = redisClient.getResource();
        //..
        String cacheContent = null;
        try {
            cacheContent = readWriteRedisClient.get("hello_world");
        } catch (Exception e) {
            //如果redis网络异常, 你可以选择重新抛出,或者忽略
        }
        if(cacheContent == null) {
            //redis中不存在,或者异常
        }
    }

  

三、基于Cluster模式的客户端

    JedisCluster实例的简单封装,内部仍然基于连接池。需要注意,cluster模式下,mget等这种multi-keys操作将不再支持。其实我们无论如何封装,都无法非常合理的解决multi-keys问题,这里涉及到K-V在集群中的迁移、节点可用性、批量操作的异常处理,特别在异常处理时,如果某个key操作异常(可能对应的redis节点异常),那么其他已经操作成功的keys、或者后续尚未操作的keys该如何处理?这个答案是模糊不清的。此外,我们JAVA开发者通常会使用线程池 + Future机制来分解multi-keys操作,其实这会带来很大的性能隐患,不建议使用。

    本人最终决定,保留redis Cluster的api方式,不做特殊封装,仅仅让其在spring环境中更易于使用即可。

    1、ClusterClientFactoryBean.java

public class ClusterClientFactoryBean implements FactoryBean<JedisCluster>,InitializingBean {

    private JedisCluster jedisCluster;

    private int maxTotal = 128;

    //最大空闲连接数
    private int maxIdle = 6;

    //最小空闲连接数
    private int minIdle = 1;
    //如果连接池耗尽,最大阻塞的时间,默认为6秒
    private long maxWait = 6000;//单位毫秒

    private int timeout = 3000;//connectionTimeout,soTimeout,默认为3秒

    private boolean testOnBorrow = true;
    private boolean testOnReturn = true;

    private String addresses;//ip:port;ip:port

    public void setMaxTotal(int maxTotal) {
        this.maxTotal = maxTotal;
    }

    public void setMaxIdle(int maxIdle) {
        this.maxIdle = maxIdle;
    }

    public void setMinIdle(int minIdle) {
        this.minIdle = minIdle;
    }

    public void setMaxWait(long maxWait) {
        this.maxWait = maxWait;
    }

    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    public void setTestOnBorrow(boolean testOnBorrow) {
        this.testOnBorrow = testOnBorrow;
    }

    public void setTestOnReturn(boolean testOnReturn) {
        this.testOnReturn = testOnReturn;
    }

    public void setAddresses(String addresses) {
        this.addresses = addresses;
    }

    protected JedisPoolConfig buildConfig() {
        JedisPoolConfig config = new JedisPoolConfig();

        config.setMinIdle(minIdle);
        config.setMaxIdle(maxIdle);
        config.setMaxTotal(maxTotal);
        config.setTestOnBorrow(testOnBorrow);
        config.setTestOnReturn(testOnReturn);
        config.setBlockWhenExhausted(true);
        config.setMaxWaitMillis(maxWait);
        config.setFairness(false);

        return config;
    }

    private Set<HostAndPort> buildHostAndPorts() {
        String[] hostPorts = addresses.split(",");
        Set<HostAndPort> hostAndPorts = new HashSet<>();
        for(String item : hostPorts) {
            String[] hostPort = item.split(":");
            HostAndPort hostAndPort = new HostAndPort(hostPort[0],Integer.valueOf(hostPort[1]));
            hostAndPorts.add(hostAndPort);
        }
        return hostAndPorts;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        JedisPoolConfig config = buildConfig();
        Set<HostAndPort> hostAndPorts = buildHostAndPorts();
        jedisCluster = new JedisCluster(hostAndPorts,timeout,config);
    }

    @Override
    public JedisCluster getObject() throws Exception {
        return jedisCluster;
    }

    @Override
    public Class<?> getObjectType() {
        return JedisCluster.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

 

    2、Spring.xml配置

    <bean id="clusterRedisClient" class="com.demo.redis.spring.ClusterClientFactoryBean">
        <property name="addresses" value="127.0.0.1:6379,127.0.01:7379,127.0.0.1:8379"/>
        <property name="maxTotal" value="256"/>
        <property name="maxIdle" value="8" />
        <property name="maxWait" value="3000" />
        <property name="timeout" value="3000" />
        <property name="minIdle" value="2" />
    </bean>

 

    3、客户端使用

    @Autowired
    private JedisCluster clusterRedisClient;
    public void test() {
        String cacheContent = null;
        try {
            cacheContent = clusterRedisClient.get("hello_world");
        } catch (Exception e) {
            //如果异常,你可以决定是否忽略
        }
        if(cacheContent == null) {
            //如果cache中不存在,或者redis异常
        }
    }

 

分享到:
评论

相关推荐

    Spring mvc整合redis实例(redis连接池)

    在Maven项目中,可以在pom.xml文件中添加`spring-data-redis`和`jedis`库,这两个库分别提供了Spring对Redis的支持和Jedis,一个Java客户端连接Redis。 ```xml &lt;groupId&gt;org.springframework.data &lt;artifactId&gt;...

    spring + redis集群

    2. Redis工具类,封装了基本的Redis操作,如存取对象、哈希操作等。 3. 示例实体类,演示如何存储和检索对象。 4. Maven配置,确保所有依赖正确导入并能正常运行项目。 部署这个项目时,确保你的环境中已经安装了...

    springboot整合Redis

    Spring Data Redis中提供了一个高度封装的类:**RedisTemplate**,针对 Jedis 客户端中大量api进行了归类封装,将同一类型操作封装为operation接口,具体分类如下: - ValueOperations:简单K-V操作 - SetOperations...

    redis相关jar包(redis2.1.5、jedis2.9.0)

    Spring Data Redis提供了一套完整的框架,使得我们可以轻松地在Spring应用中集成Redis,而Jedis则作为基础的Redis客户端,实现了对Redis命令的全面覆盖。通过合理利用这两个库,开发者可以高效地构建基于Redis的高...

    spring-data-redis 1.7.6

    总的来说,Spring-data-redis 1.7.6为Java开发者提供了一个强大且灵活的Redis客户端,通过源码我们可以更深入地理解其内部工作原理,提升我们的开发效率。无论是简单的键值操作,还是复杂的分布式解决方案,Spring-...

    spring整合redis

    对于Spring Boot项目,可以添加`spring-boot-starter-data-redis`依赖,如果不是Spring Boot项目,则需要添加`spring-data-redis`及Jedis或Lettuce客户端的依赖。 ```xml &lt;!-- Spring Boot 示例 --&gt; &lt;groupId&gt;org...

    java-redis jar

    1. 添加依赖:在Maven或Gradle的构建文件中引入对应版本的Redis客户端库和Spring Data Redis。 2. 配置Redis:在Spring的配置文件中指定Redis服务器的地址、端口、密码等信息。 3. 创建ConnectionFactory:根据配置...

    面试宝典(包含redis,springboot,springcloud,springMVC)

    本面试宝典聚焦于四个核心领域:Redis、SpringBoot、SpringCloud和SpringMVC,这些都是现代Java开发中的热门技术。下面,我们将深入探讨这些领域的关键知识点。 1. Redis: - Redis是一个高性能的键值存储系统,常...

    spring-data-redis.jar

    源码阅读可以帮助我们更深入地了解Spring Data Redis如何处理各种数据类型,以及它是如何与Redis客户端协作完成复杂操作的。 总的来说,Spring Data Redis为Java开发者提供了一个强大的工具,使得我们可以方便地...

    spring data redis 官方文档

    - **启用集群**:Spring Data Redis 支持 Redis 集群模式,可以通过简单配置实现高可用和负载均衡。 - **操作 Redis Cluster Connection**:提供了专门的方法来操作集群连接,包括节点发现、数据分区等功能。 - **...

    spring集成redis需要的jar包.rar

    Spring Data Redis使得在Spring应用中使用Redis变得更加简单,通过提供模板类、Repository支持以及与Spring的集成。Spring Data Redis的核心类是`RedisTemplate`,它封装了Jedis的操作,并且支持通过Spring的依赖...

    redis整合Spring 需要jar包

    首先,`jedis-2.9.0.jar`是连接Redis的客户端库,Jedis是Java语言编写的Redis客户端,提供了丰富的API用于操作Redis的各种数据类型。例如,你可以使用Jedis进行字符串、哈希表、列表、集合和有序集合的操作,以及...

    Java操作Redis实例,操作封装

    Jedis是Java社区广泛使用的Redis客户端库,它提供了丰富的API来与Redis服务器进行交互。本文将深入探讨如何使用Jedis进行Java操作Redis的实例,包括对象的保存、查询以及客户端连接资源管理。 首先,我们需要引入...

    spring整合redis3.2/redis2.1 jar包

    - 对于 Redis2.1,可能需要使用较早版本的 Spring Data Redis 和 Jedis 客户端库。 2. **配置 Redis**: - 在 Spring Boot 项目中,可以在 `application.properties` 或 `application.yml` 文件中配置 Redis 连接...

    Spring整合Redis

    这通常通过`RedisConnectionFactory`实现,它可以是`JedisConnectionFactory`或`LettuceConnectionFactory`,具体取决于你选择的Redis客户端库。 ```xml &lt;bean id="redisConnectionFactory" class="org.spring...

    Redis最全资料+工具+案例+详细说明(有单独使用的jedis也有结合spring使用的redis)

    在Java开发中,Redis的使用通常会涉及到Jedis库,这是一个非常流行的Java客户端,用于连接和操作Redis服务器。 首先,我们要了解如何使用Jedis。Jedis的使用主要包括连接Redis服务器、执行命令以及断开连接。例如,...

    7、spring redis 注解开发 单片机 集群 主从复制1

    它封装了Redis客户端Jedis和Lettuce,提供了基于注解的编程模型,使得操作Redis如同操作Spring的其他数据存储一样方便。 2. **Redis**:Redis是一种高性能的键值对存储系统,常用于缓存和消息队列。它支持多种数据...

    SpringDataRedis.rar

    SpringDataRedis通过提供高度封装的API,使得在Spring应用程序中集成和操作Redis变得极其便捷。 在SpringDataRedis中,主要知识点包括以下几个方面: 1. **Jedis和Lettuce客户端集成**: - SpringDataRedis支持两...

    redis架包下载

    Jedis是Java语言编写的Redis客户端,而`common-pool-1.5.5`是Apache Commons Pool库,它提供对象池服务,可以帮助优化资源管理,比如数据库连接或网络套接字。 在学习Redis时,首先你需要理解它的基本概念。Redis...

    RedisUtil方法封装类和RedisConfig配置类

    这些方法底层会调用Jedis或Lettuce等Redis客户端库,通过连接池管理连接,以确保高效且安全的访问Redis服务器。 2. **RedisConfig配置类**: `RedisConfig`是Spring Boot应用中用来配置Redis连接的类。在这个类中...

Global site tag (gtag.js) - Google Analytics