# 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