亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

[Spring cloud 一步步實現廣告系統] 14. 全量索引代碼實現

發布時間:2020-05-29 08:05:44 來源:網絡 閱讀:586 作者:zhangpan0614 欄目:編程語言

上一節我們實現了索引基本操作的類以及索引緩存工具類,本小節我們開始實現加載全量索引數據,在加載全量索引數據之前,我們需要先將數據庫中的表數據導出到一份文件中。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
        ));
    }

    /**
     * <h4>讀取全量索引加載需要的文件</h4>
     *
     * @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

在實現初始化加載全量索引的過程中,一定要保證數據加載的順序問題,因為不同的數據有可能存在著相互依賴的關聯關系,一旦順序寫錯,會造成程序報錯問題。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

松溪县| 清河县| 庆元县| 尼木县| 成安县| 南部县| 清流县| 阿克陶县| 西林县| 奈曼旗| 延安市| 南雄市| 灵山县| 定襄县| 沾化县| 永福县| 临潭县| 五家渠市| 靖安县| 尤溪县| 吴川市| 合川市| 威远县| 祁阳县| 奉新县| 瓦房店市| 无为县| 鹿泉市| 贵南县| 荥经县| 桃源县| 乌兰浩特市| 兴化市| 三都| 兴宁市| 淳安县| 连平县| 敖汉旗| 赤壁市| 宜兰县| 五常市|