原文地址:http://blog.csdn.net/tzszhzx/article/details/44590871

实现功能描述:

        redis服务器进行Master-slaver-slaver-....主从配置,通过2台sentinel进行failOver故障转移,自动切换,采用该代码完全可以直接用于实际生产环境。

       

       题外话:

         一般来说这样的部署足以支持数以百万级的用户,但如果数量实在是太高,此时redis的Master-Slaver主从不一定能够满足,因此进行redis的分片。

        本文不讲解redis的分片,但如果你使用了,需要注意的按照另一篇文章的介绍:Sentinel&Jedis看上去是个完美的解决方案,这句话只说对了一半,

         在无分片的情况是这样,但我们的应用使用了数据分片-sharing,数据被平均分布到4个不同的实例上,每个实例以主从结构部署,Jedis没有提供

         基于Sentinel的ShardedJedisPool,也就是说在4个分片中,如果其中一个分片发生主从切换,应用所使用的ShardedJedisPool无法获得通知,所有

         对那个分片的操作将会失败。文章中提出了解决方案,请参考《基于Redis Sentinel的Redis集群(主从&Sharding)高可用方案

        

 

 

        该代码模拟多线程向redis中set/get。

1、maven依赖配置

    <dependency>
        <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>1.4.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.6.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.3.2</version>
    </dependency>

2、redis.properties

# Redis settings
#sentinel1的IP和端口
im.hs.server.redis.sentinel1.host=192.168.62.154
im.hs.server.redis.sentinel1.port=26379
#sentinel2的IP和端口
im.hs.server.redis.sentinel2.host=192.168.62.153
im.hs.server.redis.sentinel2.port=26379
#sentinel的鉴权密码
im.hs.server.redis.sentinel.masterName=155Master
im.hs.server.redis.sentinel.password=hezhixiong
#最大闲置连接数
im.hs.server.redis.maxIdle=500
#最大连接数,超过此连接时操作redis会报错
im.hs.server.redis.maxTotal=5000
im.hs.server.redis.maxWaitTime=1000
im.hs.server.redis.testOnBorrow=true
#最小闲置连接数,spring启动的时候自动建立该数目的连接供应用程序使用,不够的时候会申请。
im.hs.server.redis.minIdle=300

3、spring-redis.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- Spring自动将该包目录下标记为@Service的所有类作为spring的Bean -->
    <context:component-scan base-package="com.gaojiasoft.test.redis" />

    <context:property-placeholder location="classpath:conf/redis/redis.properties" />

    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxTotal" value="${im.hs.server.redis.maxTotal}" />
        <property name="minIdle" value="${im.hs.server.redis.minIdle}" />
        <property name="maxWaitMillis" value="${im.hs.server.redis.maxWaitTime}" />
        <property name="maxIdle" value="${im.hs.server.redis.maxIdle}" />
        <property name="testOnBorrow" value="${im.hs.server.redis.testOnBorrow}" />
        <property name="testOnReturn" value="true" />
        <property name="testWhileIdle" value="true" />
    </bean>

    <bean id="sentinelConfiguration"
        class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
        <property name="master">
            <bean class="org.springframework.data.redis.connection.RedisNode">
                <property name="name" value="${im.hs.server.redis.sentinel.masterName}"></property>
            </bean>
        </property>
        <property name="sentinels">
            <set>
                <bean class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host"
                        value="${im.hs.server.redis.sentinel1.host}"></constructor-arg>
                    <constructor-arg name="port"
                        value="${im.hs.server.redis.sentinel1.port}"></constructor-arg>
                </bean>
                <bean class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host"
                        value="${im.hs.server.redis.sentinel2.host}"></constructor-arg>
                    <constructor-arg name="port"
                        value="${im.hs.server.redis.sentinel2.port}"></constructor-arg>
                </bean>
            </set>
        </property>
    </bean>

    <bean id="connectionFactory"
        class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:password="${im.hs.server.redis.sentinel.password}">
        <constructor-arg name="sentinelConfig" ref="sentinelConfiguration"></constructor-arg>
        <constructor-arg name="poolConfig" ref="poolConfig"></constructor-arg>
    </bean>

    <bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
        <property name="connectionFactory" ref="connectionFactory" />
    </bean>
</beans><strong>
</strong>

4、RedisServiceImpl.java

package com.gaojiasoft.test.redis;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

@Service("redisService")
public class RedisServiceImpl {

    private Logger logger = LoggerFactory.getLogger("RedisServiceImpl");

    @Autowired
    RedisTemplate<?, ?> redisTemplate;

    // 线程池
    private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(
            256, 256, 30L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(),
            new BasicThreadFactory.Builder().daemon(true)
                    .namingPattern("redis-oper-%d").build(),
            new ThreadPoolExecutor.CallerRunsPolicy());

    public void set(final String key, final String value) {
        redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection)
                    throws DataAccessException {
                connection.set(
                        redisTemplate.getStringSerializer().serialize(key),
                        redisTemplate.getStringSerializer().serialize(value));
                logger.debug("save key:" + key + ",value:" + value);
                return null;
            }
        });
    }

    public String get(final String key) {
        return redisTemplate.execute(new RedisCallback<String>() {
            @Override
            public String doInRedis(RedisConnection connection)
                    throws DataAccessException {
                byte[] byteKye = redisTemplate.getStringSerializer().serialize(
                        key);
                if (connection.exists(byteKye)) {
                    byte[] byteValue = connection.get(byteKye);
                    String value = redisTemplate.getStringSerializer()
                            .deserialize(byteValue);
                    logger.debug("get key:" + key + ",value:" + value);
                    return value;
                }
                logger.error("valus does not exist!,key:"+key);
                return null;
            }
        });
    }

    public void delete(final String key) {
        redisTemplate.execute(new RedisCallback<Object>() {
            public Object doInRedis(RedisConnection connection) {
                connection.del(redisTemplate.getStringSerializer().serialize(
                        key));
                return null;
            }
        });
    }

    /**
     * 线程池并发操作redis
     * 
     * @param keyvalue
     */
    public void mulitThreadSaveAndFind(final String keyvalue) {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    set(keyvalue, keyvalue);
                    get(keyvalue);
                } catch (Throwable th) {
                    // 防御性容错,避免高并发下的一些问题
                    logger.error("", th);
                }
            }
        });
    }
}

5、RedisTest.java   (Junit测试用例)

package com.gaojiasoft.test.redis;

import org.junit.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class RedisTest {

    private static ConfigurableApplicationContext context;

    RedisServiceImpl service;

    @Test
    public void testSave() throws InterruptedException {
        context = new ClassPathXmlApplicationContext(
                "classpath:conf/redis/spring-redis.xml");
        service = (RedisServiceImpl) context.getBean("redisService");

        int i = 1;
        while (true) {
            Thread.sleep(1);
            try {
                service.mulitThreadSaveAndFind("" + i);
            } catch (Exception e) {
                e.printStackTrace();
            }
            i++;
        }
    }
}

 

posted on 2019-07-04 16:32  一天不进步,就是退步  阅读(396)  评论(0编辑  收藏  举报