上一节我们实现了索引基本操作的类以及索引缓存工具类,本小节我们开始实现加载全量索引数据,在加载全量索引数据之前,我们需要先将数据库中的表数据导出到一份文件中。Let's code.

1.首先定义一个常量类,用来存储导出文件存储的目录和文件名称

因为我们导出的文件需要在搜索服务中使用到,因此,我们将文件名 & 目录以及导出对象的信息编写在mscx-ad-commom项目中。

public class FileConstant {public static final String DATA_ROOT_DIR = "/Users/xxx/Documents/promotion/data/mysql/";//各个表数据的存储文件名public static final String AD_PLAN = "ad_plan.data";public static final String AD_UNIT = "ad_unit.data";public static final String AD_CREATIVE = "ad_creative.data";public static final String AD_CREATIVE_RELARION_UNIT = "ad_creative_relation_unit.data";public static final String AD_UNIT_HOBBY = "ad_unit_hobby.data";public static final String AD_UNIT_DISTRICT = "ad_unit_district.data";public static final String AD_UNIT_KEYWORD = "ad_unit_keyword.data";
}

2.定义索引对象导出的字段信息,依然用Ad_Plan为例。

/*** AdPlanTable for 需要导出的表字段信息 => 是搜索索引字段一一对应** @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a>*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AdPlanTable {private Long planId;private Long userId;private Integer planStatus;private Date startDate;private Date endDate;
}

3.导出文件服务实现

同样,最好的实现方式就是将导出服务作为一个子工程来独立运行,我这里直接实现在了mscx-ad-db项目中

  • 定义一个空接口,为了符合我们的编码规范
/*** IExportDataService for 导出数据库广告索引初始化数据** @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a>*/
public interface IExportDataService {
}
  • 实现service
@Slf4j
@Service
public class ExportDataServiceImpl implements IExportDataService {@Autowiredprivate AdPlanRepository planRepository;/*** 导出 {@code AdPlan} from DB to File** @param fileName 文件名称*/public void exportAdPlanTable(String fileName) {List<AdPlan> planList = planRepository.findAllByPlanStatus(CommonStatus.VALID.getStatus());if (CollectionUtils.isEmpty(planList)) {return;}List<AdPlanTable> planTables = new ArrayList<>();planList.forEach(item -> planTables.add(new AdPlanTable(item.getPlanId(),item.getUserId(),item.getPlanStatus(),item.getStartDate(),item.getEndDate())));//将数据写入文件Path path = Paths.get(fileName);try (BufferedWriter writer = Files.newBufferedWriter(path)) {for (AdPlanTable adPlanTable : planTables) {writer.write(JSON.toJSONString(adPlanTable));writer.newLine();}writer.close();} catch (IOException e) {e.printStackTrace();log.error("export AdPlanTable Exception!");}}
}
  • 实现Controller,提供操作入口
@Slf4j
@Controller
@RequestMapping("/export")
public class ExportDataController {private final ExportDataServiceImpl exportDataService;@Autowiredpublic ExportDataController(ExportDataServiceImpl exportDataService) {this.exportDataService = exportDataService;}@GetMapping("/export-plan")public CommonResponse exportAdPlans() {exportDataService.exportAdPlanTable(String.format("%s%s", FileConstant.DATA_ROOT_DIR, FileConstant.AD_PLAN));return new CommonResponse();}
}
  • 结果文件内容如下,每一行都代表了一个推广计划
{"endDate":1561438800000,"planId":10,"planStatus":1,"startDate":1561438800000,"userId":10}
{"endDate":1561438800000,"planId":11,"planStatus":1,"startDate":1561438800000,"userId":10}
根据文件内容构建索引

我们在之前编写索引服务的时候,创建了一些索引需要使用的实体对象类,比如构建推广计划索引的时候,需要使用到的实体对象com.sxzhongf.ad.index.adplan.AdPlanIndexObject,可是呢,我们在上一节实现索引导出的时候,实体对象又是common 包中的com.sxzhongf.ad.common.export.table.AdPlanTable,读取出来文件中的数据只能反序列化为JSON.parseObject(p, AdPlanTable.class),我们需要将2个对象做相互映射才能创建索引信息。

1.首先我们定义一个操作类型枚举,代表我们每一次的操作类型(也需要对应到后期binlog监听的操作类型

public enum OperationTypeEnum {ADD,UPDATE,DELETE,OTHER;public static OperationTypeEnum convert(EventType type) {switch (type) {case EXT_WRITE_ROWS:return ADD;case EXT_UPDATE_ROWS:return UPDATE;case EXT_DELETE_ROWS:return DELETE;default:return OTHER;}}
}

2.因为全量索引的加载和增量索引加载的本质是一样的,全量索引其实就是一种特殊的增量索引,为了代码的可复用,我们创建统一的类来操作索引。

/*** AdLevelDataHandler for 通用处理索引类* 1. 索引之间存在层级划分,也就是相互之间拥有依赖关系的划分* 2. 加载全量索引其实是增量索引 "添加"的一种特殊实现** @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a>*/
@Slf4j
public class AdLevelDataHandler {/*** 实现广告推广计划的第二层级索引实现。* (第一级为用户层级,但是用户层级不参与索引,所以从level 2开始)* 第二层级的索引是表示 不依赖于其他索引,但是可被其他索引所依赖*/public static void handleLevel2Index(AdPlanTable adPlanTable, OperationTypeEnum type) {// 对象转换AdPlanIndexObject planIndexObject = new AdPlanIndexObject(adPlanTable.getPlanId(),adPlanTable.getUserId(),adPlanTable.getPlanStatus(),adPlanTable.getStartDate(),adPlanTable.getEndDate());//调用通用方法处理,使用IndexDataTableUtils#of来获取索引的实现类beanhandleBinlogEvent(// 在前一节我们实现了一个索引工具类,来获取注入的bean对象IndexDataTableUtils.of(AdPlanIndexAwareImpl.class),planIndexObject.getPlanId(),planIndexObject,type);}/*** 处理全量索引和增量索引的通用处理方式* K,V代表索引的键和值** @param index 索引实现代理类父级* @param key   键* @param value 值* @param type  操作类型*/private static <K, V> void handleBinlogEvent(IIndexAware<K, V> index, K key, V value, OperationTypeEnum type) {switch (type) {case ADD:index.add(key, value);break;case UPDATE:index.update(key, value);break;case DELETE:index.delete(key, value);break;default:break;}}
}

3.读取文件实现全量索引加载。

因为我们文件加载之前需要依赖另一个组件,也就是我们的索引工具类,需要添加上@DependsOn("indexDataTableUtils"),全量索引在系统启动的时候就需要加载,我们需要添加@PostConstruct来实现初始化加载,被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器调用一次。

@Component
@DependsOn("indexDataTableUtils")
public class IndexFileLoader {/*** 服务启动时,执行全量索引加载*/@PostConstructpublic void init() {//加载 推广计划List<String> adPlanStrings = loadExportedData(String.format("%s%s",FileConstant.DATA_ROOT_DIR, FileConstant.AD_PLAN));adPlanStrings.forEach(p -> AdLevelDataHandler.handleLevel2Index(JSON.parseObject(p, AdPlanTable.class), OperationTypeEnum.ADD));}/*** <h3>读取全量索引加载需要的文件</h3>** @param fileName 文件名称* @return 文件行数据*/private List<String> loadExportedData(String fileName) {try (BufferedReader reader = Files.newBufferedReader(Paths.get(fileName))) {return reader.lines().collect(Collectors.toList());} catch (IOException e) {throw new RuntimeException(e.getMessage());}}
}

Tips

在实现初始化加载全量索引的过程中,一定要保证数据加载的顺序问题,因为不同的数据有可能存在着相互依赖的关联关系,一旦顺序写错,会造成程序报错问题。

转载于:https://www.cnblogs.com/zhangpan1244/p/11324363.html

[Spring cloud 一步步实现广告系统] 14. 全量索引代码实现相关推荐

  1. [Spring cloud 一步步实现广告系统] 22. 广告系统回顾总结

    到目前为止,我们整个初级广告检索系统就初步开发完成了,我们来整体回顾一下我们的广告系统. 整个广告系统编码结构如下: 1.mscx-ad 父模块 主要是为了方便我们项目的统一管理 2.mscx-ad- ...

  2. [Spring cloud 一步步实现广告系统] 20. 系统运行测试

    系统运行 经过长时间的编码实现,我们的主体模块已经大致完成,因为之前我们都是零散的对各个微服务自行测试,接下来,我们需要将所有的服务模块进行联调测试,Let's do it. 清除测试数据&测 ...

  3. [Spring cloud 一步步实现广告系统] 13. 索引服务编码实现

    上一节我们分析了广告索引的维护有2种,全量索引加载和增量索引维护.因为广告检索是广告系统中最为重要的环节,大家一定要认真理解我们索引设计的思路,接下来我们来编码实现索引维护功能. 我们来定义一个接口, ...

  4. [Spring cloud 一步步实现广告系统] 12. 广告索引介绍

    索引设计介绍 在我们广告系统中,为了我们能更快的拿到我们想要的广告数据,我们需要对广告数据添加类似于数据库index一样的索引结构,分两大类:正向索引和倒排索引. 正向索引 通过唯一键/主键生成与对象 ...

  5. [Spring cloud 一步步实现广告系统] 21. 系统错误汇总

    广告系统学习过程中问题答疑 博客园 Eureka集群启动报错 Answer 因为Eureka在集群启动过程中,会连接集群中其他的机器进行数据同步,在这个过程中,如果别的服务还没有启动完成,就会出现Co ...

  6. [Spring cloud 一步步实现广告系统] 19. 监控Hystrix Dashboard

    在之前的18次文章中,我们实现了广告系统的广告投放,广告检索业务功能,中间使用到了 服务发现Eureka,服务调用Feign,网关路由Zuul以及错误熔断Hystrix等Spring Cloud组件. ...

  7. [Spring cloud 一步步实现广告系统] 11. 使用Feign实现微服务调用

    上一节我们使用了Ribbon(基于Http/Tcp)进行微服务的调用,Ribbon的调用比较简单,通过Ribbon组件对请求的服务进行拦截,通过Eureka Server 获取到服务实例的IP:Por ...

  8. feign响应拦截_[Spring cloud 一步步实现广告系统] 11. 使用Feign实现微服务调用

    上一节我们使用了Ribbon(基于Http/Tcp)进行微服务的调用,Ribbon的调用比较简单,通过Ribbon组件对请求的服务进行拦截,通过Eureka Server 获取到服务实例的IP:Por ...

  9. 微服务feignclient_[Spring cloud 一步步实现广告系统] 11. 使用Feign实现微服务调用

    上一节我们使用了Ribbon(基于Http/Tcp)进行微服务的调用,Ribbon的调用比较简单,通过Ribbon组件对请求的服务进行拦截,通过Eureka Server 获取到服务实例的IP:Por ...

最新文章

  1. redistemplate分布式锁实现_基于 Redis SETNX 实现分布式锁
  2. Visual SVN 非常好的转贴
  3. 在Java中怎么判断传递过来的字符串是否是数字,或者是否是字符串?(亲测)
  4. ES5-拓展 箭头函数的this、this的优先级
  5. activeperl安装不成功_SWOOLE进阶-00环境安装
  6. nginx linux windows 忽略大小写_React 基础 在 Windows 下使用 React , 你需要注意这些问题...
  7. 机器人庄园作文_2018年6月四级作文热点话题预测:高端的机器人
  8. C#制作视频播放器-player播放器组件
  9. 用户生命周期分析全攻略
  10. Linux文件中批量转换时间戳,linux 文件日期转时间戳
  11. 二阶压控电压源低通滤波器的传递函数
  12. 华为手机摄影入门到精通pdf_华为手机摄影从入门到精通
  13. 淘宝框架atlas基本使用说明
  14. html中hr怎么加文字在线里,HTML hr 标签
  15. 神器!用Python轻松搞定验证码!
  16. B站吴恩达深度学习视频笔记(1-14)——实战3:识别猫图
  17. 目录-换热器原理及计算
  18. arthas启动-attach流程
  19. 京东万象:通过以太坊联盟链解决数据流通的信任难题
  20. 电脑很流畅,但是一登qq就特别卡

热门文章

  1. linux系统/var目录的作用
  2. JAVA课程设计——“小羊吃蓝莓”小游戏
  3. django 自定义simple_tag,filter
  4. svn cleanup失败
  5. 《Linux调优工具oprofile的演示分析》
  6. CheckFail设计很垃圾
  7. HDU 4539郑厂长系列故事――排兵布阵(状压DP)
  8. 关于Opengl中将24位BMP图片加入�一个alpha通道并实现透明的问题
  9. vs2010 将.mc编译为.rc文件
  10. SliverLight Web part