Migo商城2.0 业务逻辑中添加redis 缓存(2) 二十

Migo商城2.0 业务逻辑中添加redis 缓存(2) 二十

业务逻辑中添加缓存

缓存应该添加到服务层。

  1. 查询数据库之前查询缓存
  2. 如何缓存中有,直接响应数据
  3. 如果缓存中没有查数据库
  4. 把查询结果缓存到redis
  5. 返回结果

    添加缓存的原则:

添加缓存不能影响正常的业务逻辑

因为之前没有添加带过期的set 方法,故需要在此再提下:

SETEX key seconds value

将值 value 关联到 key ,并将 key 的生存时间设为 seconds (以秒为单位)。

如果 key 已经存在, SETEX 命令将覆写旧值。

这个命令类似于以下两个命令:

1
2
SET key value
EXPIRE key seconds # 设置生存时间

不同之处是, SETEX 是一个原子性(atomic)操作,关联值和设置生存时间两个动作会在同一时间内完成,该命令在 Redis 用作缓存时,非常实用。

  • 可用版本:

    = 2.0.0

  • 时间复杂度:

    O(1)

  • 返回值:

    设置成功时返回 OK 。当 seconds 参数不合法时,返回一个错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 在 key 不存在时进行 SETEX

redis> SETEX cache_user_id 60 10086
OK

redis> GET cache_user_id # 值
"10086"

redis> TTL cache_user_id # 剩余生存时间
(integer) 49


# key 已经存在时,SETEX 覆盖旧值

redis> SET cd "timeless"
OK

redis> SETEX cd 3000 "goodbye my love"
OK

redis> GET cd
"goodbye my love"

redis> TTL cd
(integer) 2997

所以common工程中的实现为:

单机版:

1
2
3
4
5
6
7
8
9
@Override
public String set(final String key, final String value, final Integer seconds) {
return this.excute(new WorkCallback<String, Jedis>() {
@Override
public String doWorkCallback(Jedis jedis) {
return jedis.setex(key,seconds,value);
}
});
}

集群版

1
2
3
4
@Override
public String set(String key, String value, Integer seconds) {
return jedisCluster.setex(key,seconds,value);
}

后台管理系统添加缓存代码:

这里只修改一个类做下演示,毕竟只是个Demo,得抓紧时间了 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package com.migo.service;

import com.migo.pojo.CatNode;
import com.migo.pojo.ItemCat;
import com.migo.pojo.ItemCatResult;
import com.migo.utils.JsonUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


import java.util.ArrayList;
import java.util.List;

/**
* Author 知秋
* Created by kauw on 2016/11/8.
*/
@Service
public class ItemCatService extends BaseService<ItemCat> {
@Autowired
private JedisClient jedisClient;
private static final String REDIS_KEY = "MIGO_MANAGE_ITEM_CAT_LIST"; // 规则:项目名_模块名_业务名
private static final Integer REDIS_TIME = 60 * 60 * 24 * 30 * 3;

/* @Autowired
private ItemCatMapper itemCatMapper;


public List<ItemCat> getItemCatList(Long parentId) {
ItemCat example = new ItemCat();
example.setParentId(parentId);
return this.itemCatMapper.select(example);
}*/

/**
* 查询,并生成jd页面类似的树状结构
* @return
*/
public ItemCatResult getItemCatList(){

try {
// 从缓存中命中,如果命中返回,没有命中继续查询
String jsonData = jedisClient.get(REDIS_KEY);
if (StringUtils.isNotEmpty(jsonData)) {
return JsonUtils.jsonToPojo(jsonData,ItemCatResult.class);
}
} catch (Exception e) {
e.printStackTrace();
}


ItemCatResult itemCatResult=new ItemCatResult();

//调用递归方法查询商品分类列表
List catList=getItemCatList(0L);
//返回结果
itemCatResult.setCatNodes(catList);
return itemCatResult;
}

private List getItemCatList(long parentId) {
//根据parentId查询列表


ItemCat example=new ItemCat();
example.setParentId(parentId);
List<ItemCat> catList = super.queryListByWhere(example);
List resultList=new ArrayList();
int count=0;
for (ItemCat itemCat : catList) {
//如果是父节点
if (itemCat.getIsParent()) {
CatNode node=new CatNode();
node.setUrl("/products/"+itemCat.getId()+".html");
//如果当前节点为第一级节点
if (itemCat.getParentId()==0) {
node.setName("<a href='/products/"+itemCat.getId()+".html'>"+itemCat.getName()+"</a>");
}else {
node.setName(itemCat.getName());
}
node.setItems(getItemCatList(itemCat.getId()));
//将node添加到列表
resultList.add(node);
count++;
//第一层只取14条记录
if (parentId==0&&count>=14){
break;
}
}else {
//如果是最底层叶子节点
String item = "/products/"+itemCat.getId()+".html|" + itemCat.getName();
resultList.add(item);
}
}
try {
//将结果集写入到Redis中
this.jedisClient.set(REDIS_KEY,JsonUtils.objectToJson(resultList),REDIS_TIME);
} catch (Exception e) {
e.printStackTrace();
}
return resultList;
}
}

测试结果:

前端系统添加缓存:

前台门户系统添加缓存后,降低到后台系统的访问量,直接就优化了性能,也提高了用户体验度,这里拿大广告位服务做例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package com.migo.portal.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.migo.portal.pojo.Content;
import com.migo.service.JedisClient;
import com.migo.utils.HttpClientUtil;
import com.migo.utils.JsonUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
* Author 知秋
* Created by kauw on 2016/12/4.
*/
@Service
public class IndexService {
private static final String REDIS_KEY = "MIGO_PORTAL_AD1";

private static final Integer REDIS_TIME = 60 * 60 * 24;
@Autowired
private JedisClient jedisClient;


@Value("${MIGO_MANAGE_URL}")
private String MIGO_MANAGE_URL;

@Value("${AD1_URL}")
private String AD1_URL;

private static final ObjectMapper MAPPER = new ObjectMapper();

public String queryAD1(){
try {
// 从缓存中命中,如果命中返回,没有命中继续查询
String jsonData = jedisClient.get(REDIS_KEY);
if (StringUtils.isNotEmpty(jsonData)) {
return jsonData;
}
} catch (Exception e) {
e.printStackTrace();
}


try {
//调用后台管理系统的接口服务获取数据
String url=MIGO_MANAGE_URL+AD1_URL;
String jsonData = HttpClientUtil.doGet(url);
if (jsonData==null){
return null;
}
//解析json数据
JsonNode jsonNode = MAPPER.readTree(jsonData);
ArrayNode rows = (ArrayNode) jsonNode.get("rows");
List<Content> contents = JsonUtils.jsonToList(String.valueOf(rows), Content.class);
List<Map<String,Object>> result1=new ArrayList<>();
for (Content content : contents) {
Map<String,Object> map=new LinkedHashMap<>();
map.put("srcB",content.getPic());
map.put("height",240);
map.put("alt",content.getTitle());
map.put("width",670);
map.put("src",content.getPic2());
map.put("widthB",550);
map.put("href",content.getUrl());
map.put("heightB",240);
result1.add(map);
}

try {
this.jedisClient.set(REDIS_KEY,JsonUtils.objectToJson(result1),REDIS_TIME);
} catch (Exception e) {
e.printStackTrace();
}

return JsonUtils.objectToJson(result1);
} catch (Exception e) {
e.printStackTrace();
}
return null;


}
}

运行测试(成功拿到):

前后数据不同步问题解决:

这样就出现了一个问题,后台商品或者其他数据修改后,前台门户系统会出现缓存不一致的情况,这样我们可以做一个临时的解决方案,就是portal系统可以提供一个对外服务,供后台管理系统调用,假如数据修改,调用服务通知portal系统删除缓存中的相应数据即可

​ 后面会通过rabbitMQ来进一步解决此问题

您的支持将鼓励我继续创作!