# 175. 商品详情页动态渲染系统:基于 Spring Cloud 开发数据直连服务

前面多级缓存的核心思路是:如果 nginx local cache 没有,则通过 twemproxy 读本机房的从集群, 如果还是没有,则发送 http 请求给数据直连服务

那么这里数据直连的核心思路是:

  • 先找本地 ehcache(前面讲解过,这里忽略不讲解),
  • 如果没有,则走 redis 主集群,
  • 如果还是没有,则通过 fegion 拉取依赖服务的接口
  • 将数据写入主集群中,主集群会同步到各个机房的从集群
  • 同时数据直连服务将获取到的数据返回给 nginx,nginx 会写入自己本地 local cache

服务实现,创建一个服务 eshop-datalink-service

application.yml

server:
  port: 9110
logging:
  level:
    root: info
    # 启动显示 controller 中的路径映射也就是 mapping
    org.springframework.web: TRACE

spring:
  application:
    name: eshop-datalink-service
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
  redis: # 这里连接的是 twemproxy 代理地址,主机群的
    port: 1111
    host: 192.168.99.11

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

twemproxy 主集群搭建请参考

核心就在这一个请求方法中

package cn.mrcode.cache.eshop.datalinkrserver.web.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import cn.mrcode.cache.eshop.datalinkrserver.service.EshopProductService;


@RestController
@RequestMapping("/product")
public class ProductController {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private EshopProductService eshopProductService;

    @RequestMapping("/{id}")
    public String get(@PathVariable Long id) {
        // 先从本地缓存获取,这里就不写了
        // 再从 redis 主机群获取,获取逻辑参考数据聚合服务中的商品维度
        Long productId = id;
        String dimProductKey = "dim_product_" + productId;
        String dimProductJsonStr = redisTemplate.opsForValue().get(dimProductKey);
        // 如果商品聚合服务放入的 商品聚合信息没有在主机群查询到,那么就说明要么没有这个商品,要么就是过期了
        if (StringUtils.isBlank(dimProductJsonStr)) {
            // 需要访问原始服务之间获取数据,按照数据聚合服务的聚合逻辑聚合起来
            String productJsonStr = eshopProductService.findProductById(productId);
			if (StringUtils.isBlank(productJsonStr)) {
				// 没有这个商品信息
                return null;
            }
            // 否则继续获取其他维度数据
            JSONObject product = JSON.parseObject(productJsonStr);
            // 这里需要在商品服务中增加按 productId 查询的接口
            String productPropertyJsonStr = eshopProductService.findProductPropertyByProductId(productId);
            if (StringUtils.isNotBlank(productPropertyJsonStr)) {
                product.put("productProperty", JSON.parseObject(productPropertyJsonStr));
            }
            String productSpecificationJsonStr = eshopProductService.findProductSpecificationByProductId(productId);
            if (StringUtils.isNotBlank(productSpecificationJsonStr)) {
                product.put("productSpecification", JSON.parseObject(productSpecificationJsonStr));
            }
            dimProductJsonStr = product.toJSONString();
            redisTemplate.opsForValue().set(dimProductKey, dimProductJsonStr);
        }
        return dimProductJsonStr;
    }
}

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

下一章再测试,这里需要启动虚拟机中的 redis 集群,才能测试这个逻辑。