緩存的目的是為了提高系統的性能,緩存中的數據主要有兩種:
1.熱點數據。我們將經常訪問到的數據放在緩存中,降低數據庫I/O,同時因為緩存的數據的高速查詢,加快整個系統的響應速度,也在一定程度上提高並發量。
2.查詢耗時的數據。如果有一些數據查詢十分耗時,那么每次請求這些數據時,都去數據庫查詢的話,會使得系統響應速度特別低,數據庫cpu 100%。將這些數據放緩存,會極大提高系統響應速度,但同時數據實時性較差。
最近工作中有碰到需要使用緩存的情況,場景如下:app端看板統計數據匯總,在打開app時加載看板數據,匯總數據來源於不同的庫,各個數據的生成接口已經寫好,只需要去調用接口整合數據返回即可。
具體我們來看看是怎么實現的吧。
第一步,取mysql中查詢各個接口的信息:
getPanelInfo.java
1 /*service代碼略*/
2 List panels = panelService.getAllPanels(); //得到接口的名稱,接口的url
第二步,根據拿到的信息生成請求參數:
getPanelInfo.java
1 WrapResponseModel resonseModel = newWrapResponseModel();2 Map headers = new HashMap<>();3 headers.put("username", username);4 headers.put("token",token);5 List content = new ArrayList();6 for(PanelDto panelDto:panel){7 //發送http請求
8 content.add(HttpRequestUtils.get(panelDto.getUrl(), headers));9 }10 //返回格式
11 responseModel.setCode(SUCCESS_CODE);12 responseModel.setData(content);
第三步,發送http請求調用接口:
HttpRequestUtils.java
1 public static String get(String url, Mapheaders) {2 RequestConfig config =RequestConfig.custom().setConnectTimeout(TIME_OUT).setConnectionRequestTimeout(TIME_OUT).setSocketTimeout(TIME_OUT).build();3 String ret = null;4 //創建HttpClient對象
5 CloseableHttpClient closeHttpClient =HttpClients.createDefault();6 CloseableHttpResponse httpResponse = null;7 //發送get請求
8 HttpGet httpGet = newHttpGet(url);9 try{10 //add header
11 if(Objects.nonNull(headers)) {12 Set keys =headers.keySet();13 for (Iterator i =keys.iterator(); i.hasNext(); ) {14 String key =i.next();15 httpGet.addHeader(key, headers.get(key));16 }17 }18
19 httpGet.setConfig(config);20 //執行Get請求 得到Response對象
21 httpResponse =closeHttpClient.execute(httpGet);22 //httpResponse.getStatusLine() 響應頭信息
23 int httpResponseCode =httpResponse.getStatusLine().getStatusCode();24
25 if (200 !=httpResponseCode) {26 logger.error("http返回值異常, httpResponseCode = " +httpResponseCode);27 }28
29 //返回對象
30 HttpEntity httpEntity =httpResponse.getEntity();31 ret = EntityUtils.toString(httpEntity, "UTF-8");32 } catch(UnsupportedEncodingException e) {33 logger.error(e.getMessage(), e);34 } catch(ClientProtocolException e) {35 logger.error(e.getMessage(), e);36 } catch(IOException e) {37 //logger.error(e.getMessage(), e);
38 } finally{39 if (httpResponse != null) {40 try{41 httpResponse.close();42 } catch(IOException e) {43 logger.error(e.getMessage(), e);44 }45 }46 if (closeHttpClient != null) {47 try{48 closeHttpClient.close();49 } catch(IOException e) {50 logger.error(e.getMessage(), e);51 }52 }53 }54 returnret;55 }
第四步,查詢數據set進redis,之后返回查詢的數據:
getPanelInfo.java
1 if (!Objects.equals(redisCache, "false")) {
2 //redisttl過期時間3 redisProxyHandler.set(redisKey, JSON.toJSONString(responseModel), REDIS_TTL);4 logger.error("set succeed!!!!!!!!!!!!!!!!");5 }
redisHandler.java
1 public void set(String key, String value, intseconds) {2 redisCacheProvider.set(key, value, seconds);3 }
redisProvider.java
1 @Autowired2 privateJedisPool jedisPool;3
4 publicJedis getJedis() {5 Jedis jedis = this.jedisPool.getResource();6 //使用index為2的database
7 jedis.select(2);8 returnjedis;9 }10
11 public void set(String key, String value, intseconds) {12 Jedis jedis = null;13 try{14 jedis =getJedis();15 jedis.setex(key, seconds, value);16 Long exp =jedis.ttl(key);17 if (exp < 0) {18 throw new RuntimeException("data time out!");19 }20 } catch(Throwable e) {21 logger.error(e.getMessage(), e);22 throw newTokenException(e.getMessage());23 } finally{24 if(jedis != null){jedis.close;}25 }26 }
第五步,請求接口的時候,先請求redis緩存,如果命中則返回命中數據,否則,將執行上面的發送http請求在拼湊數據返回的代碼:
getPanelInfo.java
1 String panelInfo =redisProxyHandler.get(redisKey);2 Long expire =redisProxyHandler.getExpire(redisKey);3 //命中才返回,否則會去發http請求
4 if (Objects.nonNull(panelInfo) && (expire > 0) && expire
redisHandler.java
1 publicString get(String key)2 returnredisCacheProvider.get(key);3 }
redisProvider.java
1 publicString get(String key) {2 String value = null;3 Jedis jedis = null;4 try{5 jedis =getJedis();6 value =jedis.get(key);7 } catch(Throwable e) {8 logger.error(e.getMessage(), e);9 throw newTokenException(e.getMessage());10 } finally{11 if(jedis != null){12 jedis.close();13 }14 }15 returnvalue;16 }
redis相關配置文件如下
applicationContext.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
redis.properties
1 # 控制一個pool可分配多少個jedis實例2 redis.pool.maxTotal=10003 # 控制一個pool最多有多少個狀態為idle(空閑)的jedis實例4 redis.pool.maxIdle=2005 # 表示當borrow一個jedis實例時,最大的等待時間,如果超過等待時間,則直接拋出JedisConnectionException6 redis.pool.maxWaitMillis=20007 #在borrow一個jedis實例時,是否提前進行validate操作;如果為true,則得到的jedis實例均是可用的8 redis.pool.testOnBorrow=true9 # redis 單機10 # 單機 host11 jedis.host=127.0.0.112 # 單機 port13 jedis.port=6379
看了上面的代碼,我們知道一般的緩存是怎么使用的,在這個案例中,每個redisKey是根據請求的用戶名拼接特定的字符串生成的,每個請求用戶對應的key只在redis中保存一定的時間,過了指定的過期時間REDIS_TTL,數據將會被清除掉。