Kaa學習指南(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>