1. 程式人生 > >Java專案中使用Redis快取案例

Java專案中使用Redis快取案例

快取的目的是為了提高系統的效能,快取中的資料主要有兩種: 

                1.熱點資料。我們將經常訪問到的資料放在快取中,降低資料庫I/O,同時因為快取的資料的高速查詢,加快整個系統的響應速度,也在一定程度上提高併發量。

                2.查詢耗時的資料。如果有一些資料查詢十分耗時,那麼每次請求這些資料時,都去資料庫查詢的話,會使得系統響應速度特別低,資料庫cpu 100%。將這些資料放快取,會極大提高系統響應速度,但同時資料實時性較差。

  最近工作中有碰到需要使用快取的情況,場景如下:app端看板統計資料彙總,在開啟app時載入看板資料,彙總資料來源於不同的庫,各個資料的生成介面已經寫好,只需要去呼叫介面整合資料返回即可。

  具體我們來看看是怎麼實現的吧。

 第一步,取mysql中查詢各個介面的資訊:

 getPanelInfo.java

/* service程式碼略*/
List<PanelDto> panels = panelService.getAllPanels();   //得到介面的名稱,介面的url

第二步,根據拿到的資訊生成請求引數:

getPanelInfo.java

WrapResponseModel resonseModel = new WrapResponseModel();
  Map<String, String> headers = new HashMap<>();
  headers.put("username", username);
  headers.put("token",token);
  List<String> content = new ArrayList<String>();
  for(PanelDto panelDto:panel){
  //傳送http請求
       content.add(HttpRequestUtils.get(panelDto.getUrl(), headers));
}
// 返回格式
  responseModel.setCode(SUCCESS_CODE);
  responseModel.setData(content);

第三步,傳送http請求呼叫介面:

HttpRequestUtils.java

public static String get(String url, Map<String, String> headers) {
        RequestConfig config = RequestConfig.custom().setConnectTimeout(TIME_OUT).setConnectionRequestTimeout(TIME_OUT).setSocketTimeout(TIME_OUT).build();
        String ret = null;
        //建立HttpClient物件
        CloseableHttpClient closeHttpClient = HttpClients.createDefault();
        CloseableHttpResponse httpResponse = null;
        //傳送get請求
        HttpGet httpGet = new HttpGet(url);
        try {
            // add header
            if (Objects.nonNull(headers)) {
                Set<String> keys = headers.keySet();
                for (Iterator<String> i = keys.iterator(); i.hasNext(); ) {
                    String key = i.next();
                    httpGet.addHeader(key, headers.get(key));
                }
            }

            httpGet.setConfig(config);
            //執行Get請求 得到Response物件
            httpResponse = closeHttpClient.execute(httpGet);
            //httpResponse.getStatusLine() 響應頭資訊
            int httpResponseCode = httpResponse.getStatusLine().getStatusCode();

            if (200 != httpResponseCode) {
                logger.error("http返回值異常, httpResponseCode = " + httpResponseCode);
            }

            //返回物件
            HttpEntity httpEntity = httpResponse.getEntity();
            ret = EntityUtils.toString(httpEntity, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            logger.error(e.getMessage(), e);
        } catch (ClientProtocolException e) {
            logger.error(e.getMessage(), e);
        } catch (IOException e) {
            //logger.error(e.getMessage(), e);
        } finally {
            if (httpResponse != null) {
                try {
                    httpResponse.close();
                } catch (IOException e) {
                    logger.error(e.getMessage(), e);
                }
            }
            if (closeHttpClient != null) {
                try {
                    closeHttpClient.close();
                } catch (IOException e) {
                    logger.error(e.getMessage(), e);
                }
            }
        }
        return ret;
    }

第四步,查詢資料set進redis,之後返回查詢的資料:

 getPanelInfo.java

if (!Objects.equals(redisCache, "false")) {
//redisttl過期時間
redisProxyHandler.set(redisKey, JSON.toJSONString(responseModel), REDIS_TTL); logger.error("set succeed!!!!!!!!!!!!!!!!"); }

redisHandler.java

public void set(String key, String value, int seconds) {
      redisCacheProvider.set(key, value, seconds);
 }

redisProvider.java

@Autowired
    private JedisPool jedisPool;

    public Jedis getJedis() {
        Jedis jedis = this.jedisPool.getResource();
        // 使用index為2的database
        jedis.select(2);
        return jedis;
    }

    public void set(String key, String value, int seconds) {
        Jedis jedis = null;
        try {
            jedis = getJedis();
            jedis.setex(key, seconds, value);
            Long exp = jedis.ttl(key);
            if (exp < 0) {
                throw new RuntimeException("data time out!");19             }
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            throw new TokenException(e.getMessage());
        } finally {
            if(jedis != null){jedis.close;}
        }
    }

第五步,請求介面的時候,先請求redis快取,如果命中則返回命中資料,否則,將執行上面的傳送http請求在拼湊資料返回的程式碼:

getPanelInfo.java

String panelInfo = redisProxyHandler.get(redisKey);
                Long expire = redisProxyHandler.getExpire(redisKey);
               //命中才返回,否則會去發http請求
                if (Objects.nonNull(panelInfo) && (expire > 0) && expire <REDIS_TTL) {
                 responseModel = JSON.parseObject(panelInfo, WrapResponseModel.class);
                 return responseModel; 
                 }

redisHandler.java

public String get(String key) 
       return redisCacheProvider.get(key);
}

redisProvider.java

public String get(String key) {
        String value = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            value = jedis.get(key);
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            throw new TokenException(e.getMessage());
        } finally {
            if(jedis != null){
                jedis.close();
           }
        }
        return value;
    }

 redis相關配置檔案如下

applicationContext.xml


<!-- 讀取配置檔案資訊 -->
    <context:property-placeholder location="classpath:redis.properties" file-encoding="UTF-8" ignore-unresolvable="true"/>

    <!-- Jedis 連線池配置 -->
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxTotal" value="${redis.pool.maxTotal}"/>
        <property name="maxIdle" value="${redis.pool.maxIdle}"/>
        <property name="maxWaitMillis" value="${redis.pool.maxWaitMillis}"/>
        <property name="testOnBorrow" value="${redis.pool.testOnBorrow}"/>
    </bean>

    <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
        <constructor-arg ref="jedisPoolConfig"/>
        <constructor-arg value="${jedis.host}" type="java.lang.String"/>
        <constructor-arg type="int" value="${jedis.port}"/>
    </bean>

redis.properties

# 控制一個pool可分配多少個jedis例項
redis.pool.maxTotal=1000
# 控制一個pool最多有多少個狀態為idle(空閒)的jedis例項
redis.pool.maxIdle=200
# 表示當borrow一個jedis例項時,最大的等待時間,如果超過等待時間,則直接丟擲JedisConnectionException
redis.pool.maxWaitMillis=2000
#在borrow一個jedis例項時,是否提前進行validate操作;如果為true,則得到的jedis例項均是可用的
redis.pool.testOnBorrow=true
# redis 單機
# 單機 host
jedis.host=127.0.0.1
# 單機 port
jedis.port=6379