spring boot使用Jedis整合Redis



Spring Boot整合Redis有兩種方式,分別是Jedis和RedisTemplate,那麼它們二者有什麼區別呢?

1、Jedis是Redis官方推薦的面向Java操作Redis的客戶端,可以通過JedisPool來獲取Jedis連接進行set、get、del等操作,相對簡單。RedisTemplate是SpringDataRedis中對JedisAPI的高度封裝。

2、RedisTemplate相對於Jedis來說更方便更換Redis客戶端,比Jedis多了自動管理連接池的特性。


spring boot使用jedis整合redis


1、添加依賴

<!--Redis客戶端: jedis-->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.3</version>
</dependency>

2、application.yml配置Redis配置屬性

#Redis配置屬性
redis:
  #主機IP
  host: 136.50.201.130
  #Redis默認端口號
  port: 6379
  #Redis服務器連接密碼(默認爲空)
  password:
  #Redis數據庫索引(默認爲0),redis默認提供16個數據庫(0-15)
  database: 0
  #jedis連接池
  pool:
    #連接池中允許的最大連接數(默認值爲8):即能同時建立的‘最大連接個數’,連接全部用完,則需進行等待(使用負值表示沒有限制)
    max-active: 1024
    #允許池中空閒的最小連接數:低於minIdle時,將創建新的連接
    min-idle: 0
    #連接池中最多可空閒maxIdle個連接(默認值爲8) ,這裏取值爲100,表示即使沒有數據庫連接時依然可以保持100空閒的連接,而不被清除,隨時處於待命狀態
    max-idle: 100
    #等待可用連接的最大時間,單位毫秒,默認值爲-1,表示永不超時。如果超過等待時間,則直接拋出JedisConnectionException;
    max-wait: 15000
    #連接超時時間(毫秒)
    timeout: 5000

3、讀取yml文件中redis的配置屬性,注入JedisPoolConfig對象,然後根據JedisPoolConfig和redis相關配置屬性創建JedisPool連接池,再通過getResource()從JedisPool連接池獲取jedis連接對象。

package com.cd.o2o.cache;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

@Configuration //標註@Configuration的類,相當於一個xml配置文件
public class JedisPoolUtil {

    //redis配置屬性讀取
    @Value("${redis.host}")
    private String host;
    @Value("${redis.port}")
    private int port;
    @Value("${redis.database}")
    private int database;

    @Value("${redis.pool.max-active}")
    private int maxActive;
    @Value("${redis.pool.max-idle}")
    private int maxIdle;
    @Value("${redis.pool.min-idle}")
    private int minIdle;
    @Value("${redis.pool.max-wait}")
    private long maxWaitMillis;

    @Autowired
    private JedisPoolConfig jedisPoolConfig;
    @Autowired
    private JedisPool jedisPool;

    @Bean //標註@Bean後表明返回對象會被當做一個Bean註冊到Spring IoC容器
    public JedisPoolConfig createJedisPoolConfig(){
        JedisPoolConfig jedisPoolConfig = null;
        try{
            //創建連接池配置類
            jedisPoolConfig = new JedisPoolConfig();
            //控制連接池最多可以分配多少個jedis實例
            jedisPoolConfig.setMaxTotal(maxActive);
            //連接池最大空閒連接數
            jedisPoolConfig.setMaxIdle(maxIdle);
            //連接池最小空閒連接數,小於這個數會自動創建連接
            jedisPoolConfig.setMinIdle(minIdle);
            //連接池最大阻塞時間
            jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
        }catch (Exception e){
            e.printStackTrace();
        }
        return jedisPoolConfig;
    }

    @Bean
    public JedisPool createJedisPool(){
        JedisPool jedisPool = null;
        try{
            //創建jedisPool連接池
            jedisPool = new JedisPool(jedisPoolConfig,host,port,database);
        }catch (Exception e){
            e.printStackTrace();
        }
        return jedisPool;
    }

    @Bean
    public Jedis getJedis(){
        //獲取jedis連接對象
        return jedisPool.getResource();
    }
}

4、編寫redis工具類,通過jedis連接對象獲取對key進行操作,這裏我僅提供string存儲結構的操作方法。(調用jedis連接對象後,記得釋放連接)

package com.cd.o2o.cache;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.util.SafeEncoder;

import java.util.Set;

@Component  //標註@Component,表示將該類註冊到Spring IoC容器
public class JedisUtil {

    @Autowired
    private Jedis jedis;

    //操作key的方法 (用於判斷某個key是否存在,需不需要清空key裏的數據),這是對key的操作

    /**
     * 判斷某個Key是否存在
     *
     * @param key
     * @return boolean
     */
    public boolean exists(String key){
        boolean exists = false;
        try{
            //調用jedis的exists()方法,判斷某個Key是否存在於Redis服務器中
            exists = jedis.exists(key);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //關閉jedis連接,避免資源佔用
            jedis.close();
        }
        return exists;
    }


    /**
     * 查找所有給定模式的key
     *
     * @param pattern  key表達式:*表示多個 ?表示一個 (粒如:shop*表示所有以shop開頭的key)
     * @return
     */
    public Set<String> keys(String pattern){
        Set<String> set = null;
        try{
            //調用jedis的keys()方法,獲取匹配的key,並保存到Set集合中
            set = jedis.keys(pattern);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //關閉jedis連接,避免資源佔用
            jedis.close();
        }
        return set;
    }

    /**
     * 刪除key對應的記錄,可以是多個記錄
     *
     * @param keys
     * @return 刪除的記錄數
     */
    public long del(String... keys){
        long count = 0;
        try{
            //調用jedis的del()方法將相關的Keys刪除,並返回刪除的記錄數
            count = jedis.del(keys);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //關閉jedis連接,避免資源佔用
            jedis.close();
        }
        return count;
    }

    /**
     * 清除所有的key
     * @return 狀態碼
     */
    public String flushAll(){
        String state = null;
        try{
            //調用jedis的flushAll()方法,清空所有的key
            state = jedis.flushAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //關閉jedis連接,避免資源佔用
            jedis.close();
        }
        return state;
    }

    /**
     * 修改key名,如果新key已存在,則舊key的值會覆蓋新key的值
     *
     * @param oldKey
     * @param newKey
     * @return 狀態碼
     */
    public String rename(String oldKey,String newKey){
        String state = null;
        try{
            state = jedis.rename(SafeEncoder.encode(oldKey),SafeEncoder.encode(newKey));
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //關閉jedis連接,避免資源佔用
            jedis.close();
        }
        return state;
    }


    /**
     * 在新key不存在時,將舊key修改成該新key的名稱
     *
     * @param oldKey
     * @param newKey
     * @return 狀態碼
     */
    public Long renamenx(String oldKey,String newKey){
        long status = 0;
        try{
            //重命名key
            status = jedis.renamenx(oldKey,newKey);
        }catch (Exception e){
            //關閉jedis連接,避免資源佔用
            jedis.close();
        }
        return status;
    }


    //對存儲結構爲String的操作(Redis中,字符串類型的value最大可容納的數據爲512M),這是對value的操作

    /**
     * 根據key名,獲取其存儲數據
     * @param key
     * @return
     */
    public String get(String key){
        String value = null;
        try{
            //獲取指定key的存儲數據
            value = jedis.get(key);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //關閉jedis連接,避免資源佔用
            jedis.close();
        }
        return value;
    }

    /**
     * 添加記錄,如果記錄已存在則覆蓋原有的value
     * @param key
     * @param value
     * @return
     */
    public String set(String key,String value){
        String state = null;
        try{
            //設置指定key的存儲數據
            state = jedis.set(key,value);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //關閉jedis連接,避免資源佔用
            jedis.close();
        }
        return state;
    }
}

ok,相關操作已完成


  實際項目開發中,我們會先從redis中獲取指定數據,如果redis中沒有該數據,那麼我們再從mysql數據庫中取出該數據。並且爲了下次能直接從redis中取出該數據,需要將從mysql中取出的數據添加到redis中。由於前面我指定了string作爲redis的存儲結構,所以需要將從mysql中取出的數據通過json解析工具解析成string,然後再添加進redis中。

cd4356

package com.cd.o2o.service.impl;

import com.cd.o2o.cache.JedisUtil;
import com.cd.o2o.dao.AreaDao;
import com.cd.o2o.entity.Area;
import com.cd.o2o.service.AreaService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.List;

@Service("areaService")
public class AreaServiceImpl implements AreaService {

    @Autowired
    private AreaDao areaDao;
    @Autowired
    private JedisUtil jedisUtil;
    //指定key前綴
    private static final String AREALIST = "arealist";

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public List<Area> getAreaList() {
        //定義Redis的key名
        String key = AREALIST;
        //定義接收對象
        List<Area> areaList = null;
        //定義Jackson數據轉換操作類
        ObjectMapper objectMapper = new ObjectMapper();

        //判斷key是否存在
        if(!jedisUtil.exists(key)){
            //若key不存在,則從數據庫中取出相應數據,並添加進redis裏
            areaList = areaDao.queryArea();
            String jsonString = null;
            try {
                //將從數據庫中取出的實體類集合類型數據轉化成String類型數據
                jsonString = objectMapper.writeValueAsString(areaList);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
                //拋出RuntimeException才能回滾事務
                throw new RuntimeException(e.getMessage());
            }
            //將轉化後的區域信息設置進指定的key中
            jedisUtil.set(key,jsonString);
        }else {
            //若key存在,則從redis中取出該key對應的數據
            String jsonString = jedisUtil.get(key);
            //指定要將string轉化成的複雜集合類型  List<Area>
            JavaType javaType = objectMapper.getTypeFactory().constructParametricType(List.class,Area.class);
            try {
                //將json字符串轉化成List<Area>對象
                areaList = objectMapper.readValue(jsonString,javaType);
            } catch (IOException e) {
                e.printStackTrace();
                //拋出RuntimeException才能回滾事務
                throw new RuntimeException(e.getMessage());
            }
        }
        return areaList;
    }
}



總結
  1. 添加jedis依賴
  2. 在yml文件中配置redis的相關配置屬性
  3. 讀取yml中redis的配置屬性,注入JedisPoolConfig對象,然後根據JedisPoolConfig及相關屬性配置JedisPool連接池,再通過getResource()方法從JedisPool連接池中獲取jedis連接對象
  4. 調用Jedis連接對象進行set、get、del等操作
  5. 如果redis緩存中有指定數據,則從緩存中獲取,否則從mysql數據庫中獲取,再添加進redis緩存中

redis使用中經常出現 Could not get a resource from the pool 異常,解決辦法總結

spring boot基本操作

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章