Redis缓存热点数据案例

   缓存的目的是为了提高系统的性能,缓存中的数据主要有两种: 

                1.热点数据。我们将经常访问到的数据放在缓存中,降低数据库I/O,同时因为缓存的数据的高速查询,加快整个系统的响应速度,也在一定程度上提高并发量。

                2.查询耗时的数据。如果有一些数据查询十分耗时,那么每次请求这些数据时,都去数据库查询的话,会使得系统响应速度特别低,数据库cpu 100%。将这些数据放缓存,会极大提高系统响应速度,但同时数据实时性较差。

  最近工作中有碰到需要使用缓存的情况,场景如下:app端看板统计数据汇总,在打开app时加载看板数据,汇总数据来源于不同的库,各个数据的生成接口已经写好,只需要去调用接口整合数据返回即可。

  具体我们来看看是怎么实现的吧。

 

 第一步,取mysql中查询各个接口的信息:

 getPanelInfo.java

1 /* service代码略*/
2 List<PanelDto> panels = panelService.getAllPanels();   //得到接口的名称,接口的url

 

第二步,根据拿到的信息生成请求参数:

 getPanelInfo.java

 1   WrapResponseModel resonseModel = new WrapResponseModel();
 2   Map<String, String> headers = new HashMap<>();
 3   headers.put("username", username);
 4   headers.put("token",token);
 5   List<String> content = new ArrayList<String>();
 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, Map<String, String> headers) {
 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 = new HttpGet(url);
 9         try {
10             // add header
11             if (Objects.nonNull(headers)) {
12                 Set<String> keys = headers.keySet();
13                 for (Iterator<String> 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         return ret;
55     }

第四步,查询数据set进redis,之后返回查询的数据:

 getPanelInfo.java

1  if (!Objects.equals(redisCache, "false")) {
2 //redis过期时间
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, int seconds) {
2             redisCacheProvider.set(key, value, seconds);
3     }

 redisProvider.java

 1     @Autowired
 2     private JedisPool jedisPool;
 3 
 4     public Jedis getJedis() {
 5         Jedis jedis = this.jedisPool.getResource();
 6         // 使用index为2的database
 7         jedis.select(2);
 8         return jedis;
 9     }
10 
11     public void set(String key, String value, int seconds) {
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 new TokenException(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 <REDIS_TTL) {
5 responseModel = JSON.parseObject(panelInfo, WrapResponseModel.class);
6 return responseModel;
7 }

redisHandler.java

1    public String get(String key) 
2             return redisCacheProvider.get(key);
3    }

redisProvider.java

 1       public String 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 new TokenException(e.getMessage());
10         } finally {
11             if(jedis != null){
12                 jedis.close();
13            }
14         }
15         return value;
16     }

 redis相关配置文件如下

applicationContext.xml

 1  <!-- 读取配置文件信息 -->
 2     <context:property-placeholder location="classpath:redis.properties" file-encoding="UTF-8" ignore-unresolvable="true"/>
 3 
 4     <!-- Jedis 连接池配置 -->
 5     <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
 6         <property name="maxTotal" value="${redis.pool.maxTotal}"/>
 7         <property name="maxIdle" value="${redis.pool.maxIdle}"/>
 8         <property name="maxWaitMillis" value="${redis.pool.maxWaitMillis}"/>
 9         <property name="testOnBorrow" value="${redis.pool.testOnBorrow}"/>
10     </bean>
11 
12     <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
13         <constructor-arg ref="jedisPoolConfig"/>
14         <constructor-arg value="${jedis.host}" type="java.lang.String"/>
15         <constructor-arg type="int" value="${jedis.port}"/>
16     </bean>

redis.properties

 1 # 控制一个pool可分配多少个jedis实例
 2 redis.pool.maxTotal=1000
 3 # 控制一个pool最多有多少个状态为idle(空闲)的jedis实例
 4 redis.pool.maxIdle=200
 5 # 表示当borrow一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛出JedisConnectionException
 6 redis.pool.maxWaitMillis=2000
 7 #在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的
 8 redis.pool.testOnBorrow=true
 9 # redis 单机
10 # 单机 host
11 jedis.host=127.0.0.1
12 # 单机 port
13 jedis.port=6379

看了上面的代码,我们知道一般的缓存是怎么使用的,在这个案例中,每个redisKey是根据请求的用户名拼接特定的字符串生成的,每个请求用户对应的key只在redis中保存一定的时间,过了指定的过期时间REDIS_TTL,数据将会被清除掉。

posted @ 2017-12-26 10:30  jy的blog  阅读(50883)  评论(0编辑  收藏  举报