Kaa學習指南(Java版)

mxpje 8年前發布 | 54K 次閱讀 Java

來自: http://segmentfault.com/a/1190000004389762

Kaa項目學習有一個月時間, 由于是代碼量非常龐大的開源項目, 上手起來還是比較困難的, 本文章主要是要和大家分享我的學習過程,遇到的困難,以及我們應該從哪里入手閱讀代碼比較好。

環境搭建

部署Kaa官方虛擬機Demo,里面包含很多很有用的事例, 可以直接運行, 會有很直觀的感受、方便了解Kaa的各個業務, 起到很好的入門作用。

根據官方文檔從編譯代碼開始, 到安裝部署調試環境。在搭建環境中我們可以了解到Kaa是運行在JVM環境中的, 具體使用的外部組件有:Zookeeper、PostgreSQL 、MongoDB。Kaa本身有4個服務, kaa-bootstrap(集群)、 kaa-operations(集群)、 kaa-control(主備)、 kaa-admin(用戶管理界面)。我們需要了解這些服務的 具體分工 。了解PostgreSQL、MongoDB的基本查詢操作。

閱讀文檔

閱讀Kaa的 設計文檔 ,了解各個業務類型的。

閱讀代碼

從Notification Demo入手(下載源碼), 從官方給的源碼,我們可以分析出客戶端程序運行的入口。其實很簡單, 只需要如下兩行代碼, 就可以將KaaClient啟動起來。

/** new KaaClient 實例。 new DesktopKaaPlatformContext初始化Kaa上下文信息,主要是將sdk中client.properties信息同步到對象中;

  • 還有初始化4個Executor(KaaClient start時候, 執行init()才將線程實例化)。他們分別是:
  • lifeCycleExecutor(Kaa啟動依賴本線程,如果該線程執行stop其他所有任務將停止,所以說他是kaaClient的生命周期),
  • apiExecutor(),
  • callbackExecutor(),
  • scheduledExecutor(延時任務,典型例子DefaultChannelManager.onServerFailed()) **/ kaaClient = Kaa.newClient(new DesktopKaaPlatformContext()); kaaClient.start(); // 將其啟動起來</pre>

    我們閱讀KaaClient的代碼時, 要拋棄讀WebServer代碼的思想。因為這里沒有Spring, 沒有依賴注入, 只有setter,對象會傳來傳去, 傳到很深的地方,所以我們會看到很多set對象的地方。客戶端是多線程的,我們能看到很多創建線程的地方, 我們要能夠把其串聯起來。

    Kaa.newClient主要執行的代碼邏輯在其父類 AbstractKaaClient

    AbstractKaaClient(KaaClientPlatformContext context, KaaClientStateListener listener) throws IOException, GeneralSecurityException {

    /*
    * KaaClientPlatformContext 中包含:
    * 3個線程 + 1個定時任務線程 (Java Executor), 但未啟動 SimpleExecutorContext
    * */
    this.context = context;
    this.stateListener = listener;
    if (context.getProperties() != null) {
        this.properties = context.getProperties();
    } else { // 客戶端SDK示例代碼 僅僅new了一個 DesktopKaaPlatformContext, 需要初始化client properties
        /*
        *  是對client.properties 文件進行解析
        *  1.獲取client.properties
        *  2.解析bootstrap server 列表
        *  3.等其他sdk中包含的信息
        */
        this.properties = new KaaClientProperties();
    }
    
    // 傳遞 Base64 編解碼能力, BootstrapServers信息是Base64編碼記錄的
    this.properties.setBase64(context.getBase64());
    
    // 獲取bootstrapServers信息
    Map<TransportProtocolId, List<TransportConnectionInfo>> bootstrapServers = this.properties.getBootstrapServers();
    if (bootstrapServers == null || bootstrapServers.isEmpty()) {
        throw new RuntimeException("Unable to obtain list of bootstrap servers."); // NOSONAR
    }
    
    // 對bootstrapServer列表進行打亂操作
    for (Map.Entry<TransportProtocolId, List<TransportConnectionInfo>> cursor : bootstrapServers.entrySet()) {
        Collections.shuffle(cursor.getValue());
    }
    
    /*
    * kaaClientState 數據初始化. kaaClient第一次啟動是沒有status.properties文件的, 當應用關閉時, Kaa status信息會持久化到status.properties文件中
    * */
    kaaClientState = new KaaClientPropertiesState(context.createPersistentStorage(), context.getBase64(), this.properties);
    
    /*
    * transportContext 實例化, 每個transport都有的功能是生成sync請求信息, 和接收sync請求服務器端的返回信息后,交給對應的manger處理具體業務.
    * notes: sync操作就是客戶端向服務器發起請求的操作. 當請求內容要有定制的是, 這里是修改點.
    * */
    TransportContext transportContext = buildTransportContext(properties, kaaClientState);
    
    /*
    * bootstrapManager主要功能是, 向bootstrapServer獲取 operationServer的服務器列表, 和對operationServer列表的管理
    * */
    bootstrapManager = buildBootstrapManager(properties, kaaClientState, transportContext);
    
    /*
    * channelManager是對channel的管理, 目前有兩個channel, bootStrapChannel(HTTP協議, 客戶端和bootstrap交互的消息通道), operationChannel(MQTT協議, 客戶端和operationServer交互的消息通道)
    * notes: mqtt協議是包裝在TCP協議上次的協議, 實際編程就是sock編程, 只是多了一次用mqtt協議編解碼的過程.
    * */
    channelManager = buildChannelManager(bootstrapManager, bootstrapServers);
    
    /*
    * failoverManager是對channel發生故障后進行的業務操作
    * */
    failoverManager = buildFailoverManager(channelManager);
    
    /*
    * channelManager 和 failoverManager是相互依賴的
    * */
    channelManager.setFailoverManager(failoverManager);
    
    /*
    *  初始化 bootstrapChannel,operationsChannel. 將這兩個channel添加到channelManger中管理.
    *  添加channel到channelManager的channel list中之后, 每添加一個channel會啟動一個使用該channel的線程, 線程會去處理一個阻塞隊列. 也就是說會有會有地方去往這個隊列中放消息的.
    *  其實這兩個阻塞隊列就是sync操作的中轉站. 當需要向服務端獲取信息的時候, 向隊列中放不同類型TransportType的SyncTask就會自動去執行sync操作了.
    * */
    initializeChannels(channelManager, transportContext);
    
    bootstrapManager.setChannelManager(channelManager);
    bootstrapManager.setFailoverManager(failoverManager);
    
    /*
    * 如下manger是處理來著各自transport的操作. transport是來著
    * */
    profileManager = buildProfileManager(properties, kaaClientState, transportContext);
    notificationManager = buildNotificationManager(properties, kaaClientState, transportContext);
    eventManager = buildEventManager(properties, kaaClientState, transportContext);
    endpointRegistrationManager = buildRegistrationManager(properties, kaaClientState, transportContext);
    logCollector = buildLogCollector(properties, kaaClientState, transportContext);
    configurationManager = buildConfigurationManager(properties, kaaClientState, transportContext, context.getExecutorContext());
    
    /*
    * 將manager注入到transport中, 為了將manger的業務處理能力透傳給transport
    * */
    transportContext.getRedirectionTransport().setBootstrapManager(bootstrapManager);
    transportContext.getBootstrapTransport().setBootstrapManager(bootstrapManager);
    transportContext.getProfileTransport().setProfileManager(profileManager);
    transportContext.getEventTransport().setEventManager(eventManager);
    transportContext.getNotificationTransport().setNotificationProcessor(notificationManager);
    transportContext.getConfigurationTransport().setConfigurationHashContainer(configurationManager.getConfigurationHashContainer());
    transportContext.getConfigurationTransport().setConfigurationProcessor(configurationManager.getConfigurationProcessor());
    transportContext.getUserTransport().setEndpointRegistrationProcessor(endpointRegistrationManager);
    transportContext.getLogTransport().setLogProcessor(logCollector);
    transportContext.initTransports(this.channelManager, this.kaaClientState);
    
    /*
    * 構建EventFamily, 給客戶端編程暴露接口
    * */
    eventFamilyFactory = new EventFamilyFactory(eventManager, context.getExecutorContext());
    

    }</pre> </div>

 本文由用戶 mxpje 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!