# 167. 商品详情页动态渲染系统:基于 Spring Cloud 开发数据聚合服务
数据同步服务
接收各个依赖服务发送过来的某个原子数据的变更消息, 将原子数据通过 fegion 调用依赖服务的接口拉取过来存入 redis 中。
再将某个维度数据的变更消息发送到另外一个 queue 中
数据聚合服务
监听维护数据变更事件,从 redis 中将这个维度数据全部读取出来, 拼成一个大的聚合 json 串,再将这个维度数据存入 redis 中
维度分类:
- brand
- category
- product_intro
- product
业务实现,新建一个服务 eshop-dataaggr-service (端口 9108)
核心业务代码如下,这里唯一的一个多原子数据就是商品信息,看下就能明白数据聚合服务的工作原理了
package cn.mrcode.cache.eshop.dataaggrserver.rabbitmq;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queues = "aggr-data-change-queue")
public class DataChangeQueueReceiver {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@RabbitHandler
public void process(String message) {
DimEvent event = JSON.parseObject(message, DimEvent.class);
String dimType = event.getDimType();
switch (dimType) {
case "brand":
processBrandDimDataChangeMessage(event);
break;
case "category":
processCategoryDimDataChangeMessage(event);
break;
case "product":
processProductDimDataChangeMessage(event);
break;
case "product_intro":
processProductIntroDimDataChangeMessage(event);
break;
}
}
/**
* <pre>
* 只看这里觉得可能是多此一举,这里说明下:
* 1. 业务数据简化了
* 2. 实际业务中,每个维度都不可能只有一个原子数据
* 3. 比如品牌:结构多变,复杂,有很多不同的表,不同的原子数据,这里需要将一个品牌对应的多个原子数据都从 redis 中读取出来,聚合后写入 redis
* </pre>
*/
private void processBrandDimDataChangeMessage(DimEvent event) {
String key = "brand_" + event.getId();
String jsonStr = redisTemplate.opsForValue().get(key);
if (StringUtils.isBlank(jsonStr)) {
redisTemplate.delete(key);
} else {
redisTemplate.opsForValue().set("dim_" + key, jsonStr);
}
}
private void processCategoryDimDataChangeMessage(DimEvent event) {
String key = "category_" + event.getId();
String jsonStr = redisTemplate.opsForValue().get(key);
if (StringUtils.isBlank(jsonStr)) {
redisTemplate.delete(key);
} else {
redisTemplate.opsForValue().set("dim_" + key, jsonStr);
}
}
private void processProductDimDataChangeMessage(DimEvent event) {
Long productId = event.getId();
String productKey = "product_" + productId;
String productJsonStr = redisTemplate.opsForValue().get(productKey);
if (StringUtils.isBlank(productJsonStr)) {
// 主商品数据都没有的话,就直接删除这个聚合数据
redisTemplate.delete(productKey);
} else {
JSONObject product = JSON.parseObject(productJsonStr);
String productPropertyJsonStr = redisTemplate.opsForValue().get("product_property_" + productId);
if (StringUtils.isNotBlank(productPropertyJsonStr)) {
product.put("productProperty", JSON.parseObject(productPropertyJsonStr));
}
String productSpecificationJsonStr = redisTemplate.opsForValue().get("product_specification_" + productId);
if (StringUtils.isNotBlank(productSpecificationJsonStr)) {
product.put("productSpecification", JSON.parseObject(productSpecificationJsonStr));
}
redisTemplate.opsForValue().set("dim_" + productKey, product.toJSONString());
}
}
private void processProductIntroDimDataChangeMessage(DimEvent event) {
String key = "product_intro" + event.getId();
String jsonStr = redisTemplate.opsForValue().get(key);
if (StringUtils.isBlank(jsonStr)) {
redisTemplate.delete(key);
} else {
redisTemplate.opsForValue().set("product_intro" + key, jsonStr);
}
}
}
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
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