[Spring cloud 一步步实现广告系统] 14. 全量索引代码实现
上一节我们实现了索引基本操作的类以及索引缓存工具类,本小节我们开始实现加载全量索引数据,在加载全量索引数据之前,我们需要先将数据库中的表数据导出到一份文件中。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 {
@Autowired
private 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;
@Autowired
public 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来获取索引的实现类bean
handleBinlogEvent(
// 在前一节我们实现了一个索引工具类,来获取注入的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 {
/**
* 服务启动时,执行全量索引加载
*/
@PostConstruct
public 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
在实现初始化加载全量索引的过程中,一定要保证数据加载的顺序问题,因为不同的数据有可能存在着相互依赖的关联关系,一旦顺序写错,会造成程序报错问题。
[Spring cloud 一步步实现广告系统] 14. 全量索引代码实现的更多相关文章
- [Spring cloud 一步步实现广告系统] 22. 广告系统回顾总结
到目前为止,我们整个初级广告检索系统就初步开发完成了,我们来整体回顾一下我们的广告系统. 整个广告系统编码结构如下: mscx-ad 父模块 主要是为了方便我们项目的统一管理 mscx-ad-db 这 ...
- [Spring cloud 一步步实现广告系统] 20. 系统运行测试
系统运行 经过长时间的编码实现,我们的主体模块已经大致完成,因为之前我们都是零散的对各个微服务自行测试,接下来,我们需要将所有的服务模块进行联调测试,Let's do it. 清除测试数据&测 ...
- [Spring cloud 一步步实现广告系统] 13. 索引服务编码实现
上一节我们分析了广告索引的维护有2种,全量索引加载和增量索引维护.因为广告检索是广告系统中最为重要的环节,大家一定要认真理解我们索引设计的思路,接下来我们来编码实现索引维护功能. 我们来定义一个接口, ...
- [Spring cloud 一步步实现广告系统] 12. 广告索引介绍
索引设计介绍 在我们广告系统中,为了我们能更快的拿到我们想要的广告数据,我们需要对广告数据添加类似于数据库index一样的索引结构,分两大类:正向索引和倒排索引. 正向索引 通过唯一键/主键生成与对象 ...
- [Spring cloud 一步步实现广告系统] 19. 监控Hystrix Dashboard
在之前的18次文章中,我们实现了广告系统的广告投放,广告检索业务功能,中间使用到了 服务发现Eureka,服务调用Feign,网关路由Zuul以及错误熔断Hystrix等Spring Cloud组件. ...
- [Spring cloud 一步步实现广告系统] 21. 系统错误汇总
广告系统学习过程中问题答疑 博客园 Eureka集群启动报错 Answer 因为Eureka在集群启动过程中,会连接集群中其他的机器进行数据同步,在这个过程中,如果别的服务还没有启动完成,就会出现Co ...
- [Spring cloud 一步步实现广告系统] 2. 配置&Eureka服务
父项目管理 首先,我们在创建投放系统之前,先看一下我们的工程结构: mscx-ad-sponsor就是我们的广告投放系统.如上结构,我们需要首先创建一个Parent Project mscx-ad 来 ...
- [Spring cloud 一步步实现广告系统] 7. 中期总结回顾
在前面的过程中,我们创建了4个project: 服务发现 我们使用Eureka 作为服务发现组件,学习了Eureka Server,Eureka Client的使用. Eureka Server 加依 ...
- [Spring cloud 一步步实现广告系统] 1. 业务架构分析
什么是广告系统? 主要包含: 广告主投放广告的<广告投放系统> 媒体方(广告展示媒介-)检索广告用的<广告检索系统> 广告计费系统(按次,曝光量等等) 报表系统 Etc. 使用 ...
随机推荐
- 项目总结之echarts 使用
项目上需要使用echarts,对于一个新手前端来说,差点要爆炸了,自身前端基础就不好,echarts就更是不熟了,硬生生的逼着要一周做完一个系统.这算是个小总结吧,以后万一用的上捏. 渐变使用 项目中 ...
- mysql报错(Not unique table/alias)
Not unique table/alias 错误编号:1066 问题分析: SQL 语句中出现了非唯一的表或别名. 解决方法: 1.请检查出现问题位置的 SQL 语句中是否使用了相同的表名,或是定义 ...
- spring boot使用log4j2将日志写入mysql数据库
log4j2官方例子在spring boot中报错而且还是用的是org.apache.commons.dbcp包 我给改了一下使用org.apache.commons.dbcp2包 1.log4j2. ...
- 设计模式-访问者模式(Visitor)
访问者模式是行为模式的一种.访问者模式的基本想法是,软件系统中拥有一个由许多对象构成的.比较稳定的对象结构,这些对象的类都拥有一个accept方法用来接受访问者的访问.访问者是一个接口,它拥有一个vi ...
- logging,包
包 import 方式 所有对包的操作,都相当于对包下的__init__操作 from a.b.c import d b 必须 是包 import 最后只有一个层级 相对导入 . 代表当前路径, .. ...
- C# 使用表达式树获取特性的值
一.定义特性 /// <summary> /// 定义特性 /// </summary> [AttributeUsage(AttributeTargets.Field | At ...
- 基于go语言结合微信小程序开发的微商城系统
最近在慕课网上录制了一门<Golang微信小程序微商城系统原型>,这门免费课程特别适合在校大学生或者刚毕业的大学生,go语言初学者以及想要从事微商城开发项目入门的小伙伴们来学习.在课程当中 ...
- 【动态规划例题-数塔问题】-C++
描述 观察下面的数字金字塔.写一个程序查找从最高点到底部任意处结束的路径,使路径经过数字的和最大.每一步可以 从当前点走到左下方的点也可以到达右下方的点. 在上面的样例中,从13到8到26到15到24 ...
- C#3.0新增功能10 表达式树 03 支持表达式树的框架类型
连载目录 [已更新最新开发文章,点击查看详细] 存在可与表达式树配合使用的 .NET Core framework 中的类的大型列表. 可以在 System.Linq.Expressions 查 ...
- 开源项目托管到GitHub上
前提是安装了git客户端 1.进入你的GitHub账户 2.点击new repositories 创建一个新的项目 输入项目名和项目描述 3.复制该项目的https路径 4.找一个文件夹来存放 ...