微服务商城系统 实战记录:广告同步


代码见 https://github.com/betterGa/ChangGou
    
在这里插入图片描述
     可以看到,需要一个广告微服务,当它执行广告操作的时候(比如 查询),会记录 操作日志 binlog 到 MySQL,然后将 操作日志 发送给 canal ,canal 将操作记录发送给 canal 微服务 ,canal 微服务根据修改的分类 ID 调用 content 微服务查询分类对应的所有广告,canal 微服务 还会将所有广告存入到 Redis 缓存。
    

一、搭建广告微服务

    
     首先,在 changgou-service-api 中创建 changgou-service-content-api,将和数据库里的表映射的 pojo 拷贝到 API 工程中,和广告相关的表是 tb_content 和 tb_content_category。
在这里插入图片描述

     接下来,在 changgou-service 中搭建 changgou-service-content 微服务,对应的 dao、service、controller、pojo 由代码生成器生成,并导入依赖:

 		<dependency>
            <groupId>com.changgou</groupId>
            <artifactId>changgou-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.changgou</groupId>
            <artifactId>changgou-service-content-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

提供启动类:

@SpringBootApplication
@EnableEurekaClient
@MapperScan(basePackages = {
    
    "content.dao"})
public class ContentApplication {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(ContentApplication.class);
    }
}

提供 application.yml 配置文件:

server:
  port: 18084
spring:
  application:
    name: content
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://192.168.211.132:3306/changgou_content?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: 123456
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:7001/eureka
  instance:
    prefer-ip-address: true
feign:
  hystrix:
    enabled: true
mybatis:
  configuration:
    map-underscore-to-camel-case: true  #开启驼峰功能

#hystrix 配置
hystrix:
  command:
    default:
      execution:
        timeout:
        #如果enabled设置为false,则请求超时交给ribbon控制
          enabled: true
        isolation:
          strategy: SEMAPHORE

二、广告查询

     在 content 微服务中,添加 根据分类查询广告 的逻辑。

(1)业务层
     修改 changgou-service-content 的com.changgou.content.service.ContentService 接口,添加根据分类 ID 查询广告数据:

/**
     * 根据广告分类 ID 查询广告集合
     * @param id
     * @return
     */
    List<Content> findByCategory(Long id);

实现:

   /**
     * 根据 Category ID 查询广告信息
     * @param id
     * @return
     */
    @Override
    public List<Content> findByCategory(Long id) {
    
    
        Content content=new Content();
        content.setCategoryId(id);
        content.setStatus("1");
        return contentMapper.select(content);
    }

(2)控制层

   /***
     * 根据categoryId查询广告集合
     */
    @GetMapping(value = "/list/category/{id}")
    public Result<List<Content>> findByCategory(@PathVariable Long id){
    
    
        //根据分类ID查询广告集合
        List<Content> contents = contentService.findByCategory(id);
        return new Result<List<Content>>(true,StatusCode.OK,"查询成功!",contents);
    }

(3)feign 配置

     在 changgou-service-content-api 工程中添加 feign,代码如下:

@FeignClient(name="content")
@RequestMapping(value = "/content")
public interface ContentFeign {
    
    

    /***
     * 根据分类 ID 查询所有广告
     */
    @GetMapping(value = "/list/category/{id}")
    Result<List<Content>> findByCategory(@PathVariable Long id);
}

三、广告同步

在 canal 微服务中

(1)启动类中开启 feign

修改CanalApplication,添加 @EnableFeignClients 注解:
在这里插入图片描述

(2)同步实现

     修改监听类 CanalDataEventListener:

@CanalEventListener
public class CanalDataEventListener {
    
    

    @Autowired
    private ContentFeign contentFeign;
    
    //字符串
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    //自定义数据库的 操作来监听
    //destination = "example"
    @ListenPoint(destination = "example",
            schema = "changgou_content",
            table = {
    
    "tb_content", "tb_content_category"},
            eventType = {
    
    
                    CanalEntry.EventType.UPDATE,
                    CanalEntry.EventType.DELETE,
                    CanalEntry.EventType.INSERT})
    public void onEventCustomUpdate(CanalEntry.EventType eventType, CanalEntry.RowData rowData) {
    
    
        //1.获取列名 为category_id的值
        String categoryId = getColumnValue(eventType, rowData);
        //2.调用feign 获取该分类下的所有的广告集合
        Result<List<Content>> categoryresut = contentFeign.findByCategory(Long.valueOf(categoryId));
        List<Content> data = categoryresut.getData();
        //3.使用redisTemplate存储到redis中
        stringRedisTemplate.boundValueOps("content_" + categoryId).set(JSON.toJSONString(data));
    }

    private String getColumnValue(CanalEntry.EventType eventType, CanalEntry.RowData rowData) {
    
    
        String categoryId = "";
        //判断 如果是删除  则获取beforlist
        if (eventType == CanalEntry.EventType.DELETE) {
    
    
            for (CanalEntry.Column column : rowData.getBeforeColumnsList()) {
    
    
                if (column.getName().equalsIgnoreCase("category_id")) {
    
    
                    categoryId = column.getValue();
                    return categoryId;
                }
            }
        } else {
    
    
            //判断 如果是添加 或者是更新 获取afterlist
            for (CanalEntry.Column column : rowData.getAfterColumnsList()) {
    
    
                if (column.getName().equalsIgnoreCase("category_id")) {
    
    
                    categoryId = column.getValue();
                    return categoryId;
                }
            }
        }
        return categoryId;
    }
}

     可以看到,代码实现了监听广告的增删改操作,并通过 getColumnValue 方法判断了操作类型,无论是增、删、改操作,都会使用 Feign 把 根据 操作的 分类 ID 查询的广告记录 ,存入到 Redis 中。

测试:
在数据库中新增一条 category_id = 2 的记录:
在这里插入图片描述
Redis 中:
在这里插入图片描述

可以看到 category_id=2 的记录都缓存到了 Redis 中。
这时,再从数据库中删除新增的记录,看 Redis:
在这里插入图片描述

四、总结

     不论是 增、删 还是 改 (查操作就没必要同步了),思路是监控操作,一旦对 MySQL 数据库进行了增删改操作,就会通过遍历 进行了操作的记录 的每个属性,从中获取到 category_id,即 分类 ID,然后通过 Feign 调用,到 MySQL 中根据分类 ID 查询广告信息集,并把广告信息集存储到 Redis 中,因为是以 “category_xxx”(xxx 就是具体的 ID)为 key,广告信息集为 value 的形式,所以相同 key 的情况下,新的 value 会覆盖掉原先的 value,这样每次存储的就是和 MySQL 同步的数据,实现了俩数据库的一致性。相当于以 分类 ID 为维度,把数据从 MySQL 同步到 Redis 中。没有开篇示意图的那么复杂,不需要直接操作 binlog。

猜你喜欢

转载自blog.csdn.net/weixin_41750142/article/details/114364482