近年來,基於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伺服器.