摘要:各個表數(shù)據(jù)的存儲文件名定義索引對象導(dǎo)出的字段信息依然用為例。通用處理索引類索引之間存在層級劃分,也就是相互之間擁有依賴關(guān)系的劃分加載全量索引其實是增量索引添加的一種特殊實現(xiàn)若初實現(xiàn)廣告推廣計劃的第二層級索引實現(xiàn)。
上一節(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 Isaac.Zhang | 若初 */ @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項目中
定義一個空接口,為了符合我們的編碼規(guī)范
/** * IExportDataService for 導(dǎo)出數(shù)據(jù)庫廣告索引初始化數(shù)據(jù) * * @author Isaac.Zhang | 若初 */ public interface IExportDataService { }
實現(xiàn)service
@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) { ListplanList = planRepository.findAllByPlanStatus(CommonStatus.VALID.getStatus()); if (CollectionUtils.isEmpty(planList)) { return; } List 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!"); } } }
實現(xiàn)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(); } }
結(jié)果文件內(nèi)容如下,每一行都代表了一個推廣計劃
{"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 Isaac.Zhang | 若初 */ @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 staticvoid handleBinlogEvent(IIndexAware 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() { //加載 推廣計劃 ListadPlanStrings = loadExportedData(String.format("%s%s", FileConstant.DATA_ROOT_DIR, FileConstant.AD_PLAN )); adPlanStrings.forEach(p -> AdLevelDataHandler.handleLevel2Index( JSON.parseObject(p, AdPlanTable.class), OperationTypeEnum.ADD )); } /** * 讀取全量索引加載需要的文件
* * @param fileName 文件名稱 * @return 文件行數(shù)據(jù) */ private ListloadExportedData(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,會造成程序報錯問題。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/75945.html
摘要:索引設(shè)計介紹在我們廣告系統(tǒng)中,為了我們能更快的拿到我們想要的廣告數(shù)據(jù),我們需要對廣告數(shù)據(jù)添加類似于數(shù)據(jù)庫一樣的索引結(jié)構(gòu),分兩大類正向索引和倒排索引。如何在廣告系統(tǒng)中使用倒排索引核心用途是對各個維度限制的整理。 索引設(shè)計介紹 在我們廣告系統(tǒng)中,為了我們能更快的拿到我們想要的廣告數(shù)據(jù),我們需要對廣告數(shù)據(jù)添加類似于數(shù)據(jù)庫index一樣的索引結(jié)構(gòu),分兩大類:正向索引和倒排索引。 正向索引 通過...
摘要:上一節(jié)我們分析了廣告索引的維護(hù)有種,全量索引加載和增量索引維護(hù)。因為廣告檢索是廣告系統(tǒng)中最為重要的環(huán)節(jié),大家一定要認(rèn)真理解我們索引設(shè)計的思路,接下來我們來編碼實現(xiàn)索引維護(hù)功能。 上一節(jié)我們分析了廣告索引的維護(hù)有2種,全量索引加載和增量索引維護(hù)。因為廣告檢索是廣告系統(tǒng)中最為重要的環(huán)節(jié),大家一定要認(rèn)真理解我們索引設(shè)計的思路,接下來我們來編碼實現(xiàn)索引維護(hù)功能。 我們來定義一個接口,來接收所有...
摘要:在前面的過程中,我們創(chuàng)建了個服務(wù)發(fā)現(xiàn)我們使用作為服務(wù)發(fā)現(xiàn)組件,學(xué)習(xí)了的使用。加依賴加注解改配置使用項目三部曲,我們可以快速添加一個新組件,并正常使用這個我沒有在項目中實現(xiàn),但是大家可以和一樣,三部曲搞定。 在前面的過程中,我們創(chuàng)建了4個project: 服務(wù)發(fā)現(xiàn) 我們使用Eureka 作為服務(wù)發(fā)現(xiàn)組件,學(xué)習(xí)了Eureka Server,Eureka Client的使用。 Eureka...
摘要:工作流程項目依賴監(jiān)控面板引入服務(wù)調(diào)用的組件依賴引入服務(wù)消費者的依賴數(shù)據(jù)庫鏈接依賴工具類集合類操作日志監(jiān)聽解析開源工具類庫中的配置相關(guān)依賴圖片壓縮 工作流程 showImg(https://i.loli.net/2019/07/29/5d3ee1829df4d57461.png); 項目依賴 org.springframewo...
摘要:上一節(jié)我們使用了基于進(jìn)行微服務(wù)的調(diào)用,的調(diào)用比較簡單,通過組件對請求的服務(wù)進(jìn)行攔截,通過獲取到服務(wù)實例的然后再去調(diào)用。為了代碼的重用性,我們來創(chuàng)建一個新的作為的服務(wù)調(diào)用工具。 上一節(jié)我們使用了Ribbon(基于Http/Tcp)進(jìn)行微服務(wù)的調(diào)用,Ribbon的調(diào)用比較簡單,通過Ribbon組件對請求的服務(wù)進(jìn)行攔截,通過Eureka Server 獲取到服務(wù)實例的IP:Port,然后再去...
閱讀 3952·2021-11-24 09:38
閱讀 1421·2021-11-19 09:40
閱讀 2778·2021-11-18 10:02
閱讀 3691·2021-11-09 09:46
閱讀 1765·2021-09-22 15:27
閱讀 3110·2019-08-29 15:24
閱讀 997·2019-08-29 12:40
閱讀 1683·2019-08-28 18:24