Mondiran建立串連,mondiran建立

來源:互聯網
上載者:User

Mondiran建立串連,mondiran建立
以前使用jdbc建立串連的時候使用的url是這樣的形式:jdbc:mysql://hostname:port/database?key1=value1&key2=value2,在URL需要以"jabc:mysql"開頭的,其中需要聲明資料庫伺服器的地址和連接埠、資料庫名還有一些其它的屬性,以"?"分割,每一個屬性使用"&"字元分割,但是mondrian作為OLAP伺服器和mysql這類的關係型資料庫還是有所區別的,畢竟它本身不儲存任何資料,它更像是一個工具類的東西(類似於hive),能夠把MDX翻譯成一串SQL語句再交由關聯式資料庫執行,所以它不能獨立得稱為一個伺服器(在我的理解中,對於伺服器的使用只需要知道IP和port以及一些其他的參數就足夠了)。     為了能夠使用java內建的DriverManager建立connection,mondrian做了這個相容,在使用mysql這樣的資料庫的時候我們會首先執行如下的操作:

Class. forName(driver);connection = DriverManager. getConnection(url , username ,password );

getConnection函數返回的是java.sql.Connection對象,這個Connection是通用的資料庫連接,但是mondrian也是用了相同的方式建立到OLAP引擎的串連,它的driver為mondrian.olap4j.MondrianOlap4jDriver,它的url有自己獨特的結構,下面就看一下在mondrian中是如何建立串連的,除了之上的兩部,還需要再進行其他的操作:
Class. forName(driver);connection = DriverManager. getConnection(url , username ,password );OlapConnection olapConnection = connection.unwrap(OlapConnection.class);

     在mondrian中的url格式如下:jdbc:mondrian:Jdbc=jdbc:mysql://10.241.20.157:3306/foodmart?user=root&password=root;Catalog=C:\\Users\\Administrator\\Desktop\\nrtp\\FoodMart.xml;可以看出url中的每一項是通過";"分割的,類似於mysql的開頭是"jdbc:mysql",mondrian串連的url的開頭必須是"jdbc:mondrian:",另外還包括"Jdbc"和"Catalog"屬性欄位,在DriverManager的getConnection靜態方法中執行如下操作:
    public static Connection getConnection(String url,        String user, String password) throws SQLException {        java.util.Properties info = new java.util.Properties();        if (user != null) {            info.put("user", user);        }        if (password != null) {            info.put("password", password);        }        return (getConnection(url, info, Reflection.getCallerClass()));    }

這個函數是所有的資料庫連接建立的方式,其實就是建立一個Properties對象,然後加上user和password屬性,然後調用其它的getConnection方法:
    private static Connection getConnection(        String url, java.util.Properties info, Class<?> caller) throws SQLException {        /*         * When callerCl is null, we should check the application's         * (which is invoking this class indirectly)         * classloader, so that the JDBC driver class outside rt.jar         * can be loaded from here.         */        ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;        synchronized (DriverManager.class) {            // synchronize loading of the correct classloader.            if (callerCL == null) {                callerCL = Thread.currentThread().getContextClassLoader();            }        }        if(url == null) {            throw new SQLException("The url cannot be null", "08001");        }        println("DriverManager.getConnection(\"" + url + "\")");        // Walk through the loaded registeredDrivers attempting to make a connection.        // Remember the first exception that gets raised so we can reraise it.        SQLException reason = null;        for(DriverInfo aDriver : registeredDrivers) {            // If the caller does not have permission to load the driver then            // skip it.            if(isDriverAllowed(aDriver.driver, callerCL)) {                try {                    println("    trying " + aDriver.driver.getClass().getName());                    Connection con = aDriver.driver.connect(url, info);                    if (con != null) {                        // Success!                        println("getConnection returning " + aDriver.driver.getClass().getName());                        return (con);                    }                } catch (SQLException ex) {                    if (reason == null) {                        reason = ex;                    }                }            } else {                println("    skipping: " + aDriver.getClass().getName());            }        }        // if we got here nobody could connect.        if (reason != null)    {            println("getConnection failed: " + reason);            throw reason;        }        println("getConnection: no suitable driver found for "+ url);        throw new SQLException("No suitable driver found for "+ url, "08001");    }

     整了這麼多其實就是那個for語句執行真正的建立串連,前面只是設定classloader,因為在調用getConnection之前我們使用Class.forName函數只給了一個driver的類名,這裡勢必要使用反射建立一個對象,但是在這個for迴圈中遍曆了registeredDrivers對象,這是DriverManager類的一個static的List對象,但是我們在調用getConnection之前並沒有對DriverManager類做任何操作,這個對象是什麼時候放入資料的呢,這時候就要看一下在執行getConnection之前的Class.forName方法了,這個方法其實就是使用當前的classLoader將指定的類載入進來,載入類的時候會初始化這個類(部署初始化任何對象),包括初始化一些static代碼區,果然,在mondrian.olap4j.MondrianOlap4jDriver類中有這樣一段static代碼區:
    static {        try {            register();        } catch (SQLException e) {            e.printStackTrace();        }    }

     這裡register方法執行了這樣一個語句:DriverManager.registerDriver(new MondrianOlap4jDriver());哈哈,正是在這裡將這個driver註冊到DriverManager的,在registerDriver函數中,DriverManager將註冊的driver對象加入到registeredDrivers對象中的,這樣也就解開了之前的疑惑,一直在疑惑這一句forName有什麼作用。我想其它的driver類(包括mysql、oracle等)都應該使用這種方式吧。     接續看getConnection方法,它遍曆registeredDrivers列表中的每一個成員,然後嘗試用每一個driver建立connection,直到遇到第一個能夠建立成功的driver,如果都建立失敗,reason中的錯誤資訊是第一個driver建立connection的失敗資訊,真正建立connection的方法是Connection con = aDriver.driver.connect(url, info);,其中info是包含了user和password資訊的properties,接下來就再次回到MondrianOlap4jDriver類中執行建立串連了,從這裡可以看出DriverManager其實就是一個工廠,需要建立connection的時候向它註冊一個driver,建立對象的時候使用每一個具體的driver完成。MondrianOlap4jDriver類中建立connection的方法如下:
    public Connection connect(String url, Properties info) throws SQLException {        if (!MondrianOlap4jConnection.acceptsURL(url)) {            return null;        }        return factory.newConnection(this, url, info);    }

     這裡的factory是一個driver的一個成員變數,它是在driver初始化的時候建立的,在mondrian中它是根據不同的jdbc版本使用不同的factory,具體的策略是不同的版本的jdbc中包含不同的class,然後通過Class.forName判斷該class是否存在已決定當前的版本(這個策略可以省去一些版本配置的資訊),當前使用的factory是"mondrian.olap4j.FactoryJdbc41Impl",這個函數會建立並返回一個MondrianOlap4jConnectionJdbc41對象,這個對象實際建立的是他的父類對象MondrianOlap4jConnection,它的建構函式如下:    MondrianOlap4jConnection( Factory factory, MondrianOlap4jDriver driver,String url,Properties info) throws SQLException參數分別是上面提到的factory對象,driver對象。串連url和DriverManager傳遞過來的配置資訊。我覺得這個對象其實就是為了實現olap4j介面在olap4j介面和mondrian真正的connection之間做的一層轉換,建立connection主要是建立一個mondrian原生的connection,然後儲存和這個connection相關的server、schema等資訊,建立connection的語句如下:
        this.mondrianConnection = (RolapConnection) mondrian.olap.DriverManager .getConnection(list, null);

     建立mondrian串連的是通過mondrian的DriverManager實現的,其中list為解析之後的url中的key-value對和info中的配置資訊,第二個參數是CatalogLocator對象,這裡為null,實際上並沒有什麼卵用。     建立mondrian串連的第一步就是建立指定的server,在mondrian中server是用來維護和管理connection的,每次串連建立的時候會根據URL中指定的"Instance"配置決定使用哪個server(為什麼做成多個server後期再研究),如果沒有指定這個配置則使用預設的server。在mondrian中所有的server是由MondrianServerRegistry這個全域對象維護的,但是在根據Instance尋找server的時候如果找不到則會拋出異常,而不會建立新的server,通過跟蹤調用邏輯發現只有在使用XMLA服務的時候才會建立server,這裡暫不討論,假設所有的URL中都不包含Instance配置。建立server的入口為MondrianServerRegistry.createWithRepository,它需要兩個參數,分別為RepositoryContentFinder對象和CatalogLocator對象,預設的server也都是用預設的這兩個對象,其中CatalogLocator對象用來處理URL中指定的Catalog資訊,預設不做任何處理。     接著建立一個RolapConnection對象,這個對象就是mondrian內部的串連,它的建構函式為:RolapConnection(MondrianServer server,Util.PropertyList connectInfo,RolapSchema schema,DataSource dataSource),參數分別為指定的server、URL的配置資訊,使用的Schema對象,這裡為null,指定的datasource資訊,這裡為null。     每一個串連會被賦予一個全域唯一的ID,它是遞增的,然後判斷Provider配置,這個配置要麼在URL中指定為mondrian要麼不指定。接著分別提取"Catalog"、“JdbcUser”、"Jdbc"和"DataSource"配置的值,然後根據URL建立一個底層資料員的dataSource對象(DataSource是一個介面,實現了定義了getConnection函數,參數為使用者名稱和密碼),建立dataSource對象是根據URL中指定的Jdbc、JdbcUser和JdbcPassword配置完成的,另外這時還會擷取URL中指定的JdbcDrivers配置載入這裡指定的所有的driver(每一個driver使用,分割),接著再載入所有預設的drivers,包括"mondrian.jdbcDrivers", "sun.jdbc.odbc.JdbcOdbcDriver,org.hsqldb.jdbcDriver,oracle.jdbc.OracleDriver,com.mysql.jdbc.Driver"(load之前會判斷是否已經載入),接著再從URL中尋找所有資料來源jdbc串連的參數。這些參數是通過jdbc.xxx=yyy指定的,其中xxx為參數名,yyy為參數值。接著再根據URL中指定的"PoolNeeded"參數已決定是否建立一個datasource池,預設情況下是對於使用指定Jdbc建立的資料來源會建立這個池子,而對於通過DataSource參數指定的資料來源則不建立。這時候建立的DataSource對象分成了四種情況(當Jdbc和DataSource同時指定的時候使用Jdbc):1、在mondrian串連的URL中指定了Jdbc並且沒有指定PoolNeeded:這種情況下會RolapConnectionPool中根據jdbc的url和jdbc的配置資訊從池子中擷取DataSource對象(如果是mysql會加上autoReconnect=true),實際返回的是dbcp中提供的PoolingDataSource類型對象。2、在mondrian串連的URL中指定了Jdbc同時指定PoolNeeded=false:這種情況下mondrian會認為你不需要進行緩衝,因此會建立一個新的DataSource對象,類型為DriverManagerDataSource,每次getConnection的時候都是使用java提供的DriverManager老老實實地建立一個connection。3、在mondrian串連的URL中指定了DataSource並沒有指定PoolNeeded:這種情況下同樣也會使用RolapConnectionPool建立一個緩衝的DataSource對象。4、在mondrian串連的URL中指定了DataSource並指定PoolNeeded=true:這種情況下會根據是否指定使用者名稱和密碼判斷是否建立UserPasswordDataSource對象(這玩意其實是一個代理),如果都沒指定則根據DataSource參數指定的類使用該類的預設建構函式建立一個dataSource對象。     上面不管是使用哪種方式建立DataSource都將會被儲存在mondrian的connection內部成員,每次建立向資料來源的串連時都是用getConnection擷取。     建立完DataSource會將當前的mondrian的connection加入到server中,由一個map儲存,其中key為connection分配的id,value為connection對象。     接著是根據schema參數是否為null以執行不同的操作,當前schema等於null,則會建立一個statement,並加入到Locus(這個東東在mondrian中經常用到,後面再詳述)中,接著建立一個Schema對象,這個對象是這裡的重點,解析並儲存了xml檔案中定義的所有cube的資訊,等下再看,先看下建立完schema之後會解析URL中的"Role"參數,並處理當前connection的role,預設情況下使用schema的預設role(ALL)。     至此mondrian的connection就建立完成了,主要的操作就是建立DataSource對象和Schema對象,後者的建立全是通過RolapSchemaPool提高的get介面完成,在建立的時候會根據"UseSchemaPool"參數以決定是否使用schema池,預設為true,如果指定了false則建立一個新的Schema對象,否則需要根據"JdbcConnectionUuid"參數、jdbc串連的url資訊、dataSource參數以及讀取的catalog全部內容的md5值作為key從schemaPool中尋找。除此之外,如果在URL中指定了"UseContentChecksum"參數,並根據該參數判斷是否只根據catalog的全部內容的md5值作為key尋找(也就是不必使用jdbc的url作為key的一部分了),當然無論使用哪種方式從池中擷取schema,schema加入池子的時候都有一個到期時間,由"UseSchemaPool"參數指定,如果不指定則設為-1s(具體含義後面再講)。     建立schema的過程是比較複雜的,主要是要載入的schema檔案的全部內容並做一些初始化操作,本文不作講述,除此之外在建立schema的時候還會建立一個mondrian串連(其實並沒有什麼用),這裡遞迴的執行RolapConnection的建構函式(見上面),但是這時候schema已經不為null了,建立的時候會建立完DataSource之後建立一個connection,然後就返回了,並且schema中建立的這個InternalConnection並不會執行任何其他的操作,因此可以認為這一步建立connection只是為了測試資料來源的連通性。此外Schema建構函式還會建立AggTableManager對象和DataSourceChangeListener對象。     建立完schema之後再從schema檔案中(catalog內容)載入所有的cube以及相關資訊,這一部相當複雜。在建立完mondrian串連之後還需要對olap4j串連進行一些額外的設定,以後用到的時候再講。     最後對所有建立connection的URL中出現的參數進行一個總結,參數之間通過';'進行分割,如果在某一個參數的值中需要出現';'(例如jdbc的密碼,hive的jdbc串連的url中)則需要將值通過引號括起來作為一個完整的值,所有的參數都儲存在RolapConnectionProperties類中,包括:1、Provider:在mondrian中它的值要麼不指定要麼指定為mondrian2、Jdbc:指定資料來源的jdbc的url資訊。3、JdbcDrivers:指定資料來源的driver類,可以指定多個,通過','分割。4、JdbcUser:串連資料來源的使用者名稱5、JdbcPassword:串連資料來源的密碼6、Catalog:schema的地址,可以是HTTP的URL或本地檔案等。7、CatalogContent:schema的全部內容,一個xml檔案。8、CatalogName:schema的名字,當前未使用。9、DataSource:指定的DataSource類型,必須實現javax.sql.DataSource介面,在URL中必須指定Jdbc或者DataSource10、PoolNeeded:是否緩衝DataSource對象。11、Role:當前串連使用的許可權資訊。12、UseContentChecksum:是否只是用schema檔案的內容作為key尋找schema。13、UseSchemaPool:是否使用schema池,如果指定則根據前一項判斷根據什麼來作為尋找schema的key,如果為false則每次都建立一個新的schema。14、DynamicSchemaProcessor:可以指定一個類動態修改schema內容。15、Locale:設定的本地化資訊,預設使用系統的locale16、DataSourceChangeListener:可以設定一個實現mondrian.spi.DataSourceChangeListener介面的類,用於判斷資料來源是否發生改變,如果發生改變則需要更新緩衝。17、Ignore:配置是否忽略載入schema時的非致命錯誤和警告。18、Instance:指定建立connection所在的server,不指定則使用預設的server19、JdbcConnectionUuid:標識jdbc串連的id,如果指定則可以通過該配置判斷兩個jdbc的串連是否相同。20、PinSchemaTimeout:緩衝schema的到期時間,如果指定為正數則表示緩衝schema強引用的到期時間,如果到期則會被記憶體回收,如果指定為0則表示永不到期,如果指定為負數則表示將schema儲存為一個軟引用,它的絕對值為該軟引用的到期時間,這裡的時間單元可以設定為d, h, m, s, ms。21、jdbc.:jdbc參數資訊,後面指定jdbc的參數名,值為參數值。

著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.