presto 轉換靜態catlog為動態catlog

xiaoxin101發表於2020-07-22

  近年來,基於hadoop的sql框架層出不窮,presto也是其中的一員.從2012年發展至今,依然保持年輕的活力(版本迭代依然很快),presto的相關介紹,我們就不贅述了,相信看官多對presto有或多或少的瞭解,詳細的一些說明可以看官網(https://prestodb.io)的說明.

  presto自身功能和思想富有先進性,雖然由於是記憶體計算,穩定性方面還有很大提升空間,但整體依然在adhoc方面有很好的競爭力.其中在catalog載入的方式上來說比較的固化,官方並沒有做出動態的方案出來,導致在新增catalog後必須重啟整個叢集才可以將新新增的catalog資料來源新增到presto中,這無疑在實際的生產環境中很不友好.尤其是在一些中臺專案中,需要動態規劃的東西非常多.這種模式的catalog新增方式顯然不能滿足我們的開發需要.

  因此,在環境的加持下,對presto的載入catlog的方式的原始碼進行了改造,使其具有熱動態新增的功能.我們採用了外部資料庫作為他的catlog資源庫,對其進行熱載入

 (1)新增restful API請求介面.

  為了使框架本身具有新增catalog的功能,需要使其本身具有Api訪問介面的方式來來對catalog的資源進行調整的功能

1.新增CatalogResource類來實現api的請求介面
 2.新增TimiCatalogStoreConfig類來實現與資料庫互動的持久層
  3.新增TimiCatalogStore類來替換原本的catlog載入類
   4.新增CatalogInfo類來實現對catalog Model資訊的解析

#1 CatalogResource
@Path("/presto/catalog")
public class CatalogResource{

 
@GET
@Path("test")
public Response test()
{
return Response.ok("Hello world").build();
}
}

  在ServerMainModule類中setup方法,最後一行新增jaxrsBinder(binder).bind(CatalogResource.class);將新增的請求類新增進來,然後啟動主服務,並確認所開啟的presto的請求介面地址,預設埠是:8080請求http://localhost:8080/presto/catalog/test

 返回 "Hello world" 則表示restful API 介面新增成功.

#2 TimiCatalogStoreConfig 類中主要實現了讀取資料庫連線配置,以及具體執行的catalog執行動作,並使用jaxrsBinder(binder).bind(TimiCatalogStoreConfig.class);注入到專案啟動的容器中.並將Announcer,disabledCatalogs,ConnectorManager注入到類中.具體實現

public class TimiCatalogStoreConfig {

    private final Announcer announcer;
    private static final Logger log = Logger.get(TimiCatalogStoreConfig.class);
    private final Set<String> disabledCatalogs;
    private final ConnectorManager connectorManager;
public TimiCatalogStoreConfig(Announcer announcer,Set<String> disabledCatalogs,ConnectorManager connectorManager ) { this.announcer = announcer; this.disabledCatalogs = ImmutableSet.copyOf(disabledCatalogs); this.connectorManager = connectorManager; } }

  然後就是實現對catlog增刪查改動作,並將操作的結構實現到ConnectorManager中,

首先將Server中的CatalogStore替換成我們自定義實現的TimiCatalogStore並注入相關類

 @Inject
    public TimiCatalogStore(ConnectorManager connectorManager, Announcer announcer, StaticCatalogStoreConfig config,TimiCatalogStoreConfig catalogStoreConfig) {
        this(connectorManager,
                announcer,
                config.getCatalogConfigurationDir(),
                firstNonNull(config.getDisabledCatalogs(), ImmutableList.of()),
                catalogStoreConfig
        );
    }

  然後實現loadCatalogs方法,首次呼叫的時候使用load();方法載入mysql中儲存的所有catlog,然後使用ScheduledExecutorService定時的方式從mysql中提取有變化的catlog載入到presto的ConnectorManager中.

public static void updateConnectorIdAnnouncement(Announcer announcer, CatalogName connectorId)
    {
        //
        // This code was copied from PrestoServer, and is a hack that should be removed when the connectorId property is removed
        //
        // get existing announcement
        ServiceAnnouncement announcement = getPrestoAnnouncement(announcer.getServiceAnnouncements());
        // update connectorIds property
        Map<String, String> properties = new LinkedHashMap<>(announcement.getProperties());
        String property = nullToEmpty(properties.get("connectorIds"));
        Set<String> connectorIds = new LinkedHashSet<>(Splitter.on(',').trimResults().omitEmptyStrings().splitToList(property));
        connectorIds.add(connectorId.toString());
        properties.put("connectorIds", Joiner.on(',').join(connectorIds));
        // update announcement
        announcer.removeServiceAnnouncement(announcement.getId());
        announcer.addServiceAnnouncement(serviceAnnouncement(announcement.getType()).addProperties(properties).build());
        announcer.forceAnnounce();
    }

  在這裡我們設定的1分鐘從mysql庫充更新一次catalog列表

        scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                reload();
            }
        }, 60, 60, TimeUnit.SECONDS);

  呼叫reload方法定時讀取

    public void reload() {

        try{
            //獲取最新的catalogs
            Map<String, CatalogInfo> catalogInfos = catalogStoreConfig.load();

            catalogInfos.forEach(
                    (key, catalogInfo) -> {
                        if (!catalogInfoMap.containsKey(key)) {
                            //相同--catlog
                            try {
                                System.out.println("新增資料來源"+JSON.toJSONString(catalogInfo));
//                                log.info("新增資料來源:{}",JSON.toJSONString(catalogInfos.get(key)));
                                CatalogName catalogName = loadCatalog(catalogInfo);
                                updateConnectorIdAnnouncement(announcer,catalogName);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }

                        } else {
                            //不同catlog
                            if (!JSON.toJSONString(catalogInfoMap.get(key)).equals(JSON.toJSONString(catalogInfo))){
                                connectorManager.dropConnection(catalogInfo.getCatalogName());
                                try {
                                    System.out.println("新增資料來源"+JSON.toJSONString(catalogInfo));
                                    CatalogName catalogName  = loadCatalog(catalogInfo);
                                    updateConnectorIdAnnouncement(announcer,catalogName);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }

                        }
                    }
            );
            catalogInfoMap.putAll(catalogInfos);

        }catch (Exception e){
            e.printStackTrace();
        }
    }

  從mysql庫中取出來的catlog資訊和對現有的catlog進行對比,如果是不同的catlog就新增到presto中,重複的catlog不新增,刪除的catlog就從現有的catlog管理器中刪除,以此來達到動態新增catlog的動作,不需要重啟presto伺服器.

  


相關文章