您好,登錄后才能下訂單哦!
上一節(jié)我們實現(xiàn)了索引基本操作的類以及索引緩存工具類,本小節(jié)我們開始實現(xiàn)加載全量索引數(shù)據(jù),在加載全量索引數(shù)據(jù)之前,我們需要先將數(shù)據(jù)庫中的表數(shù)據(jù)導(dǎo)出到一份文件中。Let's code.
1.首先定義一個常量類,用來存儲導(dǎo)出文件存儲的目錄和文件名稱
因為我們導(dǎo)出的文件需要在搜索服務(wù)中使用到,因此,我們將文件名 & 目錄以及導(dǎo)出對象的信息編寫在
mscx-ad-commom
項目中。
public class FileConstant {
public static final String DATA_ROOT_DIR = "/Users/xxx/Documents/promotion/data/mysql/";
//各個表數(shù)據(jù)的存儲文件名
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.定義索引對象導(dǎo)出的字段信息,依然用Ad_Plan
為例。
/**
* AdPlanTable for 需要導(dǎo)出的表字段信息 => 是搜索索引字段一一對應(yīng)
*
* @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.導(dǎo)出文件服務(wù)實現(xiàn)
同樣,最好的實現(xiàn)方式就是將導(dǎo)出服務(wù)作為一個子工程來獨立運行,我這里直接實現(xiàn)在了
mscx-ad-db
項目中
/**
* IExportDataService for 導(dǎo)出數(shù)據(jù)庫廣告索引初始化數(shù)據(jù)
*
* @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a>
*/
public interface IExportDataService {
}
@Slf4j
@Service
public class ExportDataServiceImpl implements IExportDataService {
@Autowired
private AdPlanRepository planRepository;
/**
* 導(dǎo)出 {@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()
)
));
//將數(shù)據(jù)寫入文件
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!");
}
}
}
@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}
我們在之前編寫索引服務(wù)的時候,創(chuàng)建了一些索引需要使用的實體對象類,比如構(gòu)建推廣計劃索引的時候,需要使用到的實體對象com.sxzhongf.ad.index.adplan.AdPlanIndexObject
,可是呢,我們在上一節(jié)實現(xiàn)索引導(dǎo)出的時候,實體對象又是common 包中的com.sxzhongf.ad.common.export.table.AdPlanTable
,讀取出來文件中的數(shù)據(jù)只能反序列化為JSON.parseObject(p, AdPlanTable.class)
,我們需要將2個對象做相互映射才能創(chuàng)建索引信息。
1.首先我們定義一個操作類型枚舉,代表我們每一次的操作類型(也需要對應(yīng)到后期binlog監(jiān)聽的操作類型)
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.因為全量索引的加載和增量索引加載的本質(zhì)是一樣的,全量索引其實就是一種特殊的增量索引,為了代碼的可復(fù)用,我們創(chuàng)建統(tǒng)一的類來操作索引。
/**
* AdLevelDataHandler for 通用處理索引類
* 1. 索引之間存在層級劃分,也就是相互之間擁有依賴關(guān)系的劃分
* 2. 加載全量索引其實是增量索引 "添加"的一種特殊實現(xiàn)
*
* @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | 若初</a>
*/
@Slf4j
public class AdLevelDataHandler {
/**
* 實現(xiàn)廣告推廣計劃的第二層級索引實現(xiàn)。
* (第一級為用戶層級,但是用戶層級不參與索引,所以從level 2開始)
* 第二層級的索引是表示 不依賴于其他索引,但是可被其他索引所依賴
*/
public static void handleLevel2Index(AdPlanTable adPlanTable, OperationTypeEnum type) {
// 對象轉(zhuǎn)換
AdPlanIndexObject planIndexObject = new AdPlanIndexObject(
adPlanTable.getPlanId(),
adPlanTable.getUserId(),
adPlanTable.getPlanStatus(),
adPlanTable.getStartDate(),
adPlanTable.getEndDate()
);
//調(diào)用通用方法處理,使用IndexDataTableUtils#of來獲取索引的實現(xiàn)類bean
handleBinlogEvent(
// 在前一節(jié)我們實現(xiàn)了一個索引工具類,來獲取注入的bean對象
IndexDataTableUtils.of(AdPlanIndexAwareImpl.class),
planIndexObject.getPlanId(),
planIndexObject,
type
);
}
/**
* 處理全量索引和增量索引的通用處理方式
* K,V代表索引的鍵和值
*
* @param index 索引實現(xiàn)代理類父級
* @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.讀取文件實現(xiàn)全量索引加載。
因為我們文件加載之前需要依賴另一個組件,也就是我們的索引工具類,需要添加上
@DependsOn("indexDataTableUtils")
,全量索引在系統(tǒng)啟動的時候就需要加載,我們需要添加@PostConstruct
來實現(xiàn)初始化加載,被@PostConstruct
修飾的方法會在服務(wù)器加載Servlet的時候運行,并且只會被服務(wù)器調(diào)用一次。
@Component
@DependsOn("indexDataTableUtils")
public class IndexFileLoader {
/**
* 服務(wù)啟動時,執(zhí)行全量索引加載
*/
@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
));
}
/**
* <h4>讀取全量索引加載需要的文件</h4>
*
* @param fileName 文件名稱
* @return 文件行數(shù)據(jù)
*/
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
在實現(xiàn)初始化加載全量索引的過程中,一定要保證數(shù)據(jù)加載的順序問題,因為不同的數(shù)據(jù)有可能存在著相互依賴的關(guān)聯(lián)關(guān)系,一旦順序?qū)戝e,會造成程序報錯問題。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。