天天看點

Springboot2.x版本下使用redis

1、pom檔案

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
   <exclusions>
      <exclusion>
         <artifactId>lettuce-core</artifactId>
         <groupId>io.lettuce</groupId>
      </exclusion>
   </exclusions>
</dependency>      
<dependency>
   <groupId>redis.clients</groupId>
   <artifactId>jedis</artifactId>
</dependency>      

備注: 排除掉 lettuce-core用戶端工具,使用jedis工具

2、java類

RedisConfig.java----->

package fun.huanghai.redis.config;

import fun.huanghai.redis.listener.RedisMessageListener;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.Topic;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import redis.clients.jedis.JedisPoolConfig;

@Configuration
public class RedisConfig implements ApplicationContextAware{

    private ApplicationContext applicationContext;

    //任務池
    private ThreadPoolTaskScheduler taskScheduler;

    @Bean
    public RedisConnectionFactory redisConnectionFactory(){
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxWaitMillis(2000);
        poolConfig.setMaxIdle(30);
        poolConfig.setMaxTotal(50);
        RedisConnectionFactory factory = new JedisConnectionFactory(poolConfig);
        return factory;
    }

    /**
     * 配置序列化器
     * @param //redisConnectionFactory
     * @return
     */
    @Bean
    public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate<Object,Object> redisTemplate = new RedisTemplate<>();
        //RedisTemplate會自動初始化StringRedisSerializer,是以這裡直接擷取
        RedisSerializer<String> stringSerializer = redisTemplate.getStringSerializer();
        //設定字元串序列化器,這樣Spring就會把Redis的key當做字元串處理了
        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setHashKeySerializer(stringSerializer);
        redisTemplate.setValueSerializer(stringSerializer);
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        return redisTemplate;

    }

    /**
     * 建立任務池,運作線程等待處理Redis的消息
     * @return
     */
    @Bean
    public ThreadPoolTaskScheduler taskScheduler(){
        if(taskScheduler!=null){
            return taskScheduler;
        }
        taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(20);
        return taskScheduler;
    }

    /**
     * redis消息監聽容器
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory){
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        //redis連接配接工廠
        container.setConnectionFactory(redisConnectionFactory);
        //設定運作任務
        container.setTaskExecutor(taskScheduler);
        //定義監聽管道,名稱為topic1
        Topic topic = new ChannelTopic("topic1");
        //使用監聽器監聽Redis的消息
        RedisMessageListener bean = applicationContext.getBean(RedisMessageListener.class);
        container.addMessageListener(bean,topic);

        return container;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
      

RedisMessageListener.java----->

package fun.huanghai.redis.listener;

import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;

@Component
public class RedisMessageListener implements MessageListener {

    @Override
    public void onMessage(Message message, @Nullable byte[] bytes) {
        //消息體
        String body = new String(message.getBody());
        //管道名稱
        String topic = new String(bytes);
        System.out.println(body);
        System.out.println(topic);
    }
}
      

RedisController.java---->

package fun.huanghai.redis.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.Jedis;

import java.util.*;

@RequestMapping("/redis")
@RestController
public class RedisController {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    //使用Redis的字元串(string)和哈希(hash)
    @GetMapping("/stringAndHash")
    public Map<String,Object> testStringAndHash(){
        Map<String,Object> map = new HashMap<>();

        redisTemplate.opsForValue().set("key1","value1");
        //注意這裡使用JDK的序列化器,是以Redis儲存時不是整數,不能運算
        redisTemplate.opsForValue().set("int_key","1");
        stringRedisTemplate.opsForValue().set("int","1");

        //使用運算
        stringRedisTemplate.opsForValue().increment("int",1);

        //擷取底層Jedis連接配接
        Jedis jedis = (Jedis) stringRedisTemplate.getConnectionFactory()
                .getConnection()
                .getNativeConnection();

        //減一操作,這個指令RedisTemplate不支援,是以我先擷取底層的連接配接在操作
        jedis.decr("int");
        Map<String,String> hash = new HashMap<>();
        hash.put("field1","value1");
        hash.put("field2","value2");
        //存入一個散列資料類型
        stringRedisTemplate.opsForHash().putAll("hash",hash);
        //新增一個字段
        stringRedisTemplate.opsForHash().put("hash","field3","value3");
        //綁定散列操作的key,這樣可以連續對同一個散列資料類型進行操作
        BoundHashOperations<String, Object, Object> hashOps = stringRedisTemplate.boundHashOps("hash");
        //删除2個字段
        hashOps.delete("field1","field2");
        //新增一個字段
        hashOps.put("field4","value4");

        map.put("success",true);

        return map;
    }

    //使用Redis的連結清單(清單)
    @RequestMapping("/list")
    public Map<String,Object> testList(){
        //插入2個清單,注意它們在連結清單的順序
        //連結清單從左到右順序為v10,v8,v6,v4,v2
        stringRedisTemplate.opsForList().leftPushAll("list1","v2","v4",
                "v6","v8","v10");
        //連結清單從左到右順序為v1,v2,v3,v4,v5,v6
        stringRedisTemplate.opsForList().rightPushAll("list2","v1","v2",
                "v3","v4","v5","v6");
        //綁定list2連結清單操作
        BoundListOperations<String, String> listOps = stringRedisTemplate.boundListOps("list2");

        //從右邊彈出一個成員  v1,v2,v3,v4,v5
        Object r1 = listOps.rightPop();
        //擷取定位元素,Redis從0開始計算,這裡值為v2
        Object r2 = listOps.index(1);
        //從左邊插傳入連結表 v0,v1,v2,v3,v4,v5
        listOps.leftPush("v0");
        //求連結清單長度 6
        Long size = listOps.size();
        //求連結清單下标區間成員,整個連結清單下标範圍為0到size-1,這裡不取最後一個元素 v0,v1,v2,v3,v4
        List<String> elements = listOps.range(0, size - 2);


        Map<String,Object> map = new HashMap<>();
        map.put("success",true);
        map.put("list",elements);
        map.put("r1",r1);
        map.put("r2",r2);
        map.put("size",size);

        return map;
    }

    //使用Redis的集合
    @GetMapping("/set")
    public Map<String,Object> testSet(){
        //請注意:這裡v1重複2次,因為集合不允許重複,是以隻是插入5個成員到集合中
        stringRedisTemplate.opsForSet().add("set1","v1","v1",
                "v2","v3","v4","v5");

        stringRedisTemplate.opsForSet().add("set2","v2","v4","v6","v8");

        //綁定set1集合操作
        BoundSetOperations<String, String> setOps = stringRedisTemplate.boundSetOps("set1");
        //增加2個元素 v1,v2,v3,v4,v5,v6,v7
        setOps.add("v6","v7");
        //删除2個元素 v2,v3,v4,v5,v6
        setOps.remove("v1","v7");
        //傳回所有元素 v2,v3,v4,v5,v6
        Set<String> set1 = setOps.members();
        //求成員數 5
        Long size = setOps.size();
        //求交集 v2,v4,v6
        Set<String> set2 = setOps.intersect("set2");
        //求交集,并且用新集合inter儲存 v2,v4,v6
        setOps.intersectAndStore("set2","inter");
        //求差集 v3,v5
        Set<String> diff = setOps.diff("set2");
        //求差集,并且用新集合diff儲存
        setOps.diffAndStore("set2","diff");
        //求并集 v2,v3,v4,v5,v6,v8
        Set<String> union = setOps.union("set2");
        //求并集,并且用新集合union儲存
        setOps.unionAndStore("set2","union");


        Map<String,Object> map = new HashMap<>();
        map.put("success",true);
        map.put("set1",set1);
        map.put("size",size);
        map.put("inter",set2);
        map.put("diff",diff);
        map.put("union",union);
        return map;
    }

    //使用Redis的有序結合
    @GetMapping("/zset")
    public Map<String,Object> testZset(){
        Set<ZSetOperations.TypedTuple<String>> typedTupleSet = new HashSet<>();
        for (int i = 0; i < 9; i++) {
            //分數
            double scope = i*0.1;
            //建立一個TypedTuple對象,存入值和分數
            ZSetOperations.TypedTuple<String> typedTuple = new DefaultTypedTuple<>("value"+i,scope);
            typedTupleSet.add(typedTuple);
        }
        //往有序結合插入元素
        stringRedisTemplate.opsForZSet().add("zset1",typedTupleSet);
        //綁定zset1有序結合操作
        BoundZSetOperations<String, String> zSetOps = stringRedisTemplate.boundZSetOps("zset1");
        //增加一個元素
        zSetOps.add("value10",0.26);
        Set<String> setRange = zSetOps.range(1, 6);
        //按分數排序擷取有序集合
        Set<String> setScope = zSetOps.rangeByScore(0.2, 0.6);
        //定義值範圍
        RedisZSetCommands.Range range = new RedisZSetCommands.Range();
        range.gt("value3");//大于value3
        //range.gte("value3");//大于等于value3
        //range.lt("value8");//小于value8
        range.lte("value8");//小于等于value8
        //按值排序,請注意這個排序是按字元串排序
        Set<String> setLex = zSetOps.rangeByLex(range);
        //删除元素
        zSetOps.remove("value9","value2");
        //求分數
        Double score = zSetOps.score("value8");
        //在下标區間下,按分數排序,同時傳回value和score
        Set<ZSetOperations.TypedTuple<String>> scoreSet = zSetOps.rangeWithScores(1, 6);
        //按從大到小排序
        Set<String> reverseSet = zSetOps.reverseRange(2, 8);


        Map<String,Object> map = new HashMap<>();
        map.put("success",true);
        map.put("setRange",setRange);
        map.put("setScope",setScope);
        map.put("setLex",setLex);
        map.put("scoreSet",scoreSet);
        map.put("reverseSet",reverseSet);
        map.put("score",score);
        return map;
    }

    //使用Redis流水線測試性能
    @RequestMapping("/multi")
    public Map<String,Object> testMulti(){
        redisTemplate.opsForValue().set("key1","value1");


        List list = (List) redisTemplate.execute(new SessionCallback() {
            @Nullable
            @Override
            public Object execute(RedisOperations ro) throws DataAccessException {
                //設定要監控的key1
                ro.watch("key1");
                //開啟事務,在exec指令執行前,全部都隻是進入隊列
                ro.multi();;
                ro.opsForValue().set("key2","vlue2");
                ro.opsForValue().increment("key1",1);
                //擷取值将為null,因為redis隻是把指令放入隊列
                Object key2 = ro.opsForValue().get("key2");
                System.out.println("指令在隊列,是以value為null【"+key2+"】");
                ro.opsForValue().set("key3","value3");
                Object key3 = ro.opsForValue().get("key3");
                System.out.println("指令在隊列,是以value為null【"+key3+"】");
                //執行exec指令,将先判别key1是否在監控後被修改過,如果是則不執行事務,否則就執行事務
                return ro.exec();

            }
        });

        System.out.println(list);

        Map<String,Object> map = new HashMap<>();
        map.put("success",true);
        return map;
    }

    //使用Redis流水線測試性能
    @RequestMapping("/pipeline")
    public Map<String,Object> testPipeline(){

        long start = System.currentTimeMillis();
        List list = redisTemplate.executePipelined(new SessionCallback<Object>() {
            @Nullable
            @Override
            public Object execute(RedisOperations ro) throws DataAccessException {
                for (int i = 0; i < 10000; i++) {
                    ro.opsForValue().set("pipeline_"+i,"value_"+i);
                    String value = (String) ro.opsForValue().get("pipeline_"+i);
                    if(i==10000){
                        System.out.println("指令隻是進入隊列,是以值為空【"+value+"】");
                    }
                }

                return null;
            }
        });

        long end = System.currentTimeMillis();
        System.out.println("耗時:"+(end-start)+"毫秒。");
        Map<String,Object> map = new HashMap<>();
        map.put("success",true);
        return map;
    }

    /**
     * 發送消息到redis中(訂閱模式)
     * @param body
     * @return
     */
    @GetMapping("/message")
    public Map<String,Object> testMessage(String body){

        redisTemplate.convertAndSend("topic1",body);
        Map<String,Object> map = new HashMap<>();
        map.put("success",true);
        return map;
    }

    //使用lua腳本
    @GetMapping("/lua")
    public Map<String,Object> testLua(){
        DefaultRedisScript<String> redisScript = new DefaultRedisScript<>();
        //設定腳本
        redisScript.setScriptText("return 'Hello Redis'");
        //定義傳回類型,注意: 如果沒有這個定義,Spring不會傳回結果
        redisScript.setResultType(String.class);
        RedisSerializer stringSerializer = redisTemplate.getStringSerializer();
        //執行lua腳本
        Object obj = redisTemplate.execute(redisScript, stringSerializer, stringSerializer, null);
        Map<String,Object> map = new HashMap<>();
        map.put("success",true);
        map.put("res",obj);
        return map;
    }

    //使用lua腳本
    @GetMapping("/lua2")
    public Map<String,Object> testLua2(String k1,String k2,String v1,String v2){
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        //設定腳本
        String lua = "redis.call('set',KEYS[1],ARGV[1])\n" +
                "redis.call('set',KEYS[2],ARGV[2])\n" +
                "local str1 = redis.call('get',KEYS[1])\n" +
                "local str2 = redis.call('get',KEYS[2])\n" +
                "if str1 == str2 then\n" +
                "return 1\n" +
                "end\n" +
                "return 0";
        redisScript.setScriptText(lua);
        //定義傳回類型,注意: 如果沒有這個定義,Spring不會傳回結果

        redisScript.setResultType(Long.class);
        RedisSerializer stringSerializer = redisTemplate.getStringSerializer();
        //定義key參數
        List<String> keyList = new ArrayList<>();
        keyList.add(k1);
        keyList.add(k2);
        //傳遞2個參數值,其中第一個序列化器是key的序列化器,第二個序列化器是參數的序列化器
        Object obj = redisTemplate.execute(redisScript, stringSerializer, stringSerializer, keyList,v1,v2);
        Map<String,Object> map = new HashMap<>();
        map.put("success",true);
        map.put("res",obj);
        return map;
    }
}
      

備注:因為redis預設是連接配接 localhost:6379,是以沒有配置位址和端口号了  (基本常用的redis操作都在這了)