1. 程式人生 > >Microsoft.Extensions.DependencyInjection 之三:展開測試

Microsoft.Extensions.DependencyInjection 之三:展開測試

目錄

  • 前文回顧
    • IServiceCallSite
    • CallSiteFactory
    • ServiceProviderEngine
    • CompiledServiceProviderEngine
    • DynamicServiceProviderEngine
  • 測試引數
    • -m|method
    • -t|target
    • -n|number
    • -c|cache
    • -l|loop
  • 啟動測試
  • 測試結果
    • 反射
    • 表示式樹
    • Emit 與表示式差異不大
    • 開銷對比

前文回顧

Microsoft.Extensions.DependencyInjection 之一:解析實現 提到了 Microsoft.Extensions.DependencyInjection 包含以下核心元件。

IServiceCallSite

元件例項化上下文,包含許多實現,僅列舉其中的ConstantCallSiteCreateInstanceCallSiteConstructorCallSite

如下。

ConstructorCallSite既是IServiceCallSite的實現類,又複合引用IServiceCallSite,以表達自身引數的構建形式。

元件生命週期也使用IServiceCallSite表達,它們既從IServiceCallSite繼承,也引用IServiceCallSite

CallSiteFactory

當元件需要被例項化時,CallSiteFactory從維護的ServiceDescriptor查詢注入方式,對型別注入的元件使用反射解析其建構函式,並遞迴解析其引數,最後快取得到的IServiceCallSite例項。

ServiceProviderEngine

ServiceProviderEngine是抽象類,內部依賴CallSiteRuntimeResolver完成基於反射的元件例項化,並快取了元件例項化的委託。

CompiledServiceProviderEngine

CompiledServiceProviderEngineServiceProviderEngine繼承,內部依賴ExpressionResolverBuilder完成基於表示式樹的元件例項化的委託。

DynamicServiceProviderEngine

DynamicServiceProviderEngineCompiledServiceProviderEngine繼承,它建立的委託比較特殊:

  • 該委託第1次執行實際是 ServiceProviderEngine 內部的CallSiteRuntimeResolver呼叫
  • 該委託第2次執行時開啟非同步任務,呼叫CompiledServiceProviderEngine內部的ExpressionResolverBuilder編譯出委託並覆蓋ServiceProviderEngine內部快取。

為了印證該邏輯,這裡使用 LINQPad 6 進行探索,該程式碼可以在控制檯中執行,但部分語句僅在 LINQPad 中生效。

void Main() {
    var services = new ServiceCollection()
        .AddTransient<IFoo1, Foo1>()
        .BuildServiceProvider();
    var engine = ReflectionExtensions.GetNonPublicField(services, "_engine");
    var realizedServices = (System.Collections.IDictionary)ReflectionExtensions.GetNonPublicProperty(engine, "RealizedServices");

    for (int i = 0; i < 3; i++) {
        services.GetRequiredService<IFoo1>(); //元件例項化
        foreach (DictionaryEntry item in realizedServices) {
            var title = String.Format("Loop {0}, type {1}, hash {2}", i, ((Type)item.Key).FullName, item.Value.GetHashCode());
            item.Value.Dump(title, depth: 2); //僅被 LINQPad 支援
        }
        Thread.Sleep(10); //確保非同步任務完成
    }
}

class ReflectionExtensions {
    public static Object GetNonPublicField(Object instance, String name) {
        var type = instance.GetType();
        var field = type.GetField(name, BindingFlags.NonPublic | BindingFlags.Instance);
        return field.GetValue(instance);
    }

    public static Object GetNonPublicProperty(Object instance, String name) {
        var type = instance.GetType();
        var property = type.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Instance);
        return property.GetValue(instance);
    }
}

interface IFoo1 {
    void Hello();
}

class Foo1 : IFoo1 {
    public void Hello() {
        Console.WriteLine("Foo1.Hello()");
    }
}

執行該指令碼,可以看到

  • 第1次和第2次元件例項化,委託相同,hash 值都是 688136691,都是Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine的內部委託;
  • 第1次和第2次元件例項化的 callSite計數從1談到2;
  • 第3次元件例項例,委託變成了System.Object lambda_method(...),hash 值變成了 1561307880;

LINQPad 5 執行該指令碼未能看到 hash 值變化,猜測是優化相關所致,考慮到DEBUG、單元測試和 LINQPad 6 已經實證,不再研究。

Microsoft.Extensions.DependencyInjection 之二:使用診斷工具觀察記憶體佔用 對比了元件例項化前後的記憶體變化如下圖,從第3次開始元件例項化的效能大幅提升。

在以上基礎上得到作了小結:

Microsoft.Extensions.DependencyInjection 並非是銀彈,它的便利性是一種空間換時間的典型,我們需要對以下情況有所瞭解:

  • 重度使用依賴注入的大型專案啟動過程相當之慢;
  • 如果單次請求需要例項化的元件過多,前期請求的記憶體開銷不可輕視;
  • 由於例項化伴隨著遞迴呼叫,過深的依賴將不可避免地導致堆疊溢位;

測試引數

Microsoft.Extensions.DependencyInjection 中抽象類Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine有以下實現

  • Microsoft.Extensions.DependencyInjection.ServiceLookup.CompiledServiceProviderEngine
  • Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine
  • Microsoft.Extensions.DependencyInjection.ServiceLookup.ExpressionsServiceProviderEngine
  • Microsoft.Extensions.DependencyInjection.ServiceLookup.RuntimeServiceProviderEngine
  • Microsoft.Extensions.DependencyInjection.ServiceLookup.ILEmitServiceProviderEngine

RuntimeServiceProviderEngine 是對 ServiceProviderEngine 的原樣繼承可以忽略,ExpressionsServiceProviderEngineCompiledServiceProviderEngine都是表示式樹的使用也沒有差異。ILEmitServiceProviderEngine是 emit 相關的實現。

從aspnet/DependencyInjection 獲取到的分支release/2.1 後,提取到ServiceProviderEngineCompiledServiceProviderEngineILEmitServiceProviderEngine等核心實現,再編寫控制檯程式,對依賴注入中反射、表示式樹、emit 3種實現方式的開銷和效能上進行探索。

程式使用啟動引數控制組件例項化行為,並記錄測試結果,以下是程式啟動引數的解釋。

-m|method

例項化方式,使用反射、表示式樹與 Emit 參與了測試,分別對應:

  • ref:使用反射例項化元件,實質是對 CallSiteRuntimeResolver 的呼叫;
  • exp:使用表示式樹例項化元件,實質是對 ExpressionResolverBuilder 的呼叫;
  • emit:使用 emit 例項化元件,實質是對 ILEmitResolverBuilder 的呼叫;
Action<Type, Boolean> handle = default;
if (method == "ref")
{
    handle = GetRefService;
}
else if (method == "exp")
{
    handle = GetExpService;
}
else if (method == "emit")
{
    handle = GetEmitService;
}

-t|target

例項化目標,使用選取以下兩種,配合引數 -n|number 使用

  • foo:使用 IFoo_{n} 作為例項化目標,已定義了 IFoo_0、IFoo_1、IFoo_2 至 IFoo_9999 共1萬個介面與對應實現
  • bar:使用 IBar_{n} 作為例項化目標,只定義了 IBar_100、IBar_1000、IBar_5000、IBar_10000 共4個介面,每個實現均以 IFoo 作為建構函式的引數,
    • IBar_100:使用 IFoo_0、IFoo_1 至 IFoo_99 作為建構函式引數;
    • IBar_1000:使用 IFoo_0、IFoo_1 至 IFoo_999 作為建構函式引數;
    • IBar_5000:使用 IFoo_0、IFoo_1 至 IFoo_4999 作為建構函式引數;
    • IBar_10000:使用 IFoo_0、IFoo_1 至 IFoo_9999 作為建構函式引數;

該部分同 大量介面與實現類的生成 一樣仍然使用指令碼生成。

-n|number

幫助指示例項化的目標及數量

  • 100:target = foo 為從 IFoo_0、IFoo_1 至 IFoo_100 共100個介面,target =bar 則僅為 IBar_100;
  • 1000:target = foo 為從 IFoo_0、IFoo_1 至 IFoo_1000 共1000個介面,target = bar 則僅為 IBar_1000;
  • 5000:target = foo 為從 IFoo_0、IFoo_1 至 IFoo_5000 共5000個介面,target =bar 則僅為 IBar_5000;
  • 10000:target = foo 為從 IFoo_0、IFoo_1 至 IFoo_10000 共10000個介面,target =bar 則僅為 IBar_10000;

-c|cache

快取行為,cache = false 時每次都構建委託,cache = true 則把構建委託快取起來重複使用。GetRefService()實現如下,GetExpService()GetEmitService()相似。

static void GetRefService(Type type, Boolean cache)
{
    var site = _expEngine.CallSiteFactory.CreateCallSite(type, new CallSiteChain());
    Func<ServiceProviderEngineScope, object> func;
    if (cache)
    {
        func = _expEngine.RealizedServices.GetOrAdd(type, scope => _expEngine.RuntimeResolver.Resolve(site, scope));
    }
    else
    {
        func = scope => _expEngine.RuntimeResolver.Resolve(site, scope);
        _expEngine.RealizedServices[type] = func;
    }
    if (func == null)
    {
        _logger.Warn("Cache miss");
        return;
    }
    var obj = func(_expEngine.Root);
    if (obj == null)
    {
        throw new NotImplementedException();
    }
}

-l|loop

重複執行若干次,每次均記錄測試時長

static void TestBar(Action<Type, Boolean> handle, String method, Boolean cache, Type type)
{
    _watch.Restart();
    handle(type, cache);
    _watch.Stop();
    _logger.Info("method {0}, cache {1}, target {2}, cost {3}",
        method, cache, type.Name, _watch.ElapsedMilliseconds);
}

...
TestBar(handle, method, false, number);
for (int i = 1; i < loop; i++)
{
    TestBar(handle, method, cache, number);
}

由於本測試的重點是對比使用反射、表示式樹與 emit 的效能與開銷,故程式啟動後首先遍歷 ServiceCollection 對每個元件呼叫 CallSiteFactory.CreateCallSite(Type serviceType),確保元件的上下文已經被建立和快取。

啟動測試

對以上引數進行組合,得到以下啟動方式,測試結果非同步寫入日誌檔案供後續解析。

# 啟用委託快取行為,例項化以 IFoo_ 作為命名字首注入的服務
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m ref -t foo -c true -n 100 -l 100
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m ref -t foo -c true -n 1000 -l 100
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m ref -t foo -c true -n 5000 -l 100
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m ref -t foo -c true -n 10000 -l 100
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m exp -t foo -c true -n 100 -l 100
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m exp -t foo -c true -n 1000 -l 100
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m exp -t foo -c true -n 5000 -l 100
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m exp -t foo -c true -n 10000 -l 100
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m emit -t foo -c true -n 100 -l 100
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m emit -t foo -c true -n 1000 -l 100
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m emit -t foo -c true -n 5000 -l 100
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m emit -t foo -c true -n 10000 -l 100

# 禁用委託快取行為,例項化以 IFoo_ 作為命名字首注入的服務
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m ref -t foo -c false -n 100 -l 50
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m ref -t foo -c false -n 1000 -l 50
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m ref -t foo -c false -n 5000 -l 50
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m ref -t foo -c false -n 10000 -l 50
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m exp -t foo -c false -n 100 -l 50
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m exp -t foo -c false -n 1000 -l 50
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m exp -t foo -c false -n 5000 -l 50
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m exp -t foo -c false -n 10000 -l 50
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m emit -t foo -c false -n 100 -l 50
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m emit -t foo -c false -n 1000 -l 50
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m emit -t foo -c false -n 5000 -l 50
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m emit -t foo -c false -n 10000 -l 50

# 啟用委託快取行為,例項化 IBar_100、IBar_1000、IBar_5000、IBar_10000
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m ref -t bar -c true -n 100 -l 100
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m ref -t bar -c true -n 1000 -l 100
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m ref -t bar -c true -n 5000 -l 100
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m ref -t bar -c true -n 10000 -l 100
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m exp -t bar -c true -n 100 -l 100
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m exp -t bar -c true -n 1000 -l 100
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m exp -t bar -c true -n 5000 -l 100
# ./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m exp -t bar -c true -n 10000 -l 100 # 請求無法完成,丟擲 IL 相關異常
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m emit -t bar -c true -n 100 -l 100
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m emit -t bar -c true -n 1000 -l 100
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m emit -t bar -c true -n 5000 -l 100
# ./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m emit -t bar -c true -n 10000 -l 100 # 請求無法完成,丟擲 IL 相關異常

# 禁用委託快取行為,例項化 IBar_100、IBar_1000、IBar_5000、IBar_10000
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m ref -t bar -c false -n 100 -l 50
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m ref -t bar -c false -n 1000 -l 50
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m ref -t bar -c false -n 5000 -l 50
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m ref -t bar -c false -n 10000 -l 50
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m exp -t bar -c false -n 100 -l 50
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m exp -t bar -c false -n 1000 -l 50
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m exp -t bar -c false -n 5000 -l 50
# ./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m exp -t bar -c false -n 10000 -l 50 # 請求無法完成,丟擲 IL 相關異常
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m emit -t bar -c false -n 100 -l 50
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m emit -t bar -c false -n 1000 -l 50
./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m emit -t bar -c false -n 5000 -l 50
# ./LeoninewAxe.Scaffold.DependencyInjection.App.exe -m emit -t bar -c false -n 10000 -l 50 # 請求無法完成,丟擲 IL 相關異常

值得一提的是,表示式樹和 emit 均無法完成 IBar_10000 的例項化,執行中丟擲相同異常 "System.InvalidProgramException: The JIT compiler encountered invalid IL code or an internal limitation."

測試結果

使用 LINQPad 編寫指令碼解析日誌,對解析結果使用 Excel 透視作表,得到耗時平均值與標準差。

對於本測試使用到的以 IFoo_ 和 IBar_ 作為命名字首的介面來說:

  • 1萬餘介面的注入時間為 10s 左右;
  • 1萬餘介面的元件的上下文建立時間在 0.6s 左右;
  • 開啟委託快取時,所有例項化方式都能獲益;
  • 所有方式例項化 IBar_N 均比例項化 IFoo_0 至 IFoo_N 快非常多;

反射

  • 對快取不敏感(控制檯為 dotnet 3.0 版本);
  • 元件數量增長時,記憶體使用平穩,完成10000個 IFoo 例項化完成後程序記憶體增長 49.1MB-35.9MB=13.2MB;

表示式樹

  • 隨著元件數量增長,對快取越發敏感;
  • 記憶體需求增長,完成10000個 IFoo 例項化後進程記憶體增長75.7MB-35.9MB=39.8MB;
  • 例項化依賴眾多的元件時,在快取下的耗時幾乎忽略;

Emit 與表示式差異不大

  • 同表示式樹對快取敏感
  • 記憶體需求增長,完成10000個 IFoo 例項化後進程記憶體增長77.7MB-35.9MB=41.8MB;
  • 耗時更不穩定

開銷對比

對比1:開啟快取,例項化 IFoo_ 相關元件

對比2:開啟快取,例項化 IBar_ 相關元件

表達樹與 emit 方式均無法完成例項化 IBar_10000

對比3:關閉快取,例項化 IFoo_ 相關元件

對比4:關閉快取,例項化 IBar_ 相關元件

表達樹與 emit 方式均無法完成例項化 IBar_10000

相對於使用反射來說,不開啟快取時表示式樹和 emit 既慢記憶體消耗又高——無論是例項化 IFoo_ 相關元件還是 IBar_ 相關元件,它們均達到更高的記憶體佔用,又頻繁地觸發 GC,最終 CPU 使用率居高不下。

測試中未使用 GC.SuppressFinalize()處理例項化得到的元件,大量的 IFoo_ 例項回收影響判斷,IBar_ 沒有這個問題故放出截圖。

相關推薦

Microsoft.Extensions.DependencyInjection 展開測試

目錄 前文回顧 IServiceCallSite CallSiteFactory ServiceProviderEngine CompiledServiceProviderEngine

測試開發linux面試後臺進程操作

狀態 很好 分配 例如 名稱 標識 批處理 推薦 子進程 Hi,大家好我是Tom,繼上次分享之後這次給大家帶來新的知識。 進程是Linux系統中一個非常重要的概念。Linux是一個多任務的操作系統,系統上經常同時運行著多個進程。我們不關心這些進程究竟是如何分配的,或者是內核

Kubernetes下web服務的效能測試三部曲橫向擴容

本章是《Kubernetes下web服務的效能測試三部曲》系列的終篇,之前我們用AB和JMeter兩種工具壓測了k8s環境下的Tomcat,並通過調整記憶體和CPU來驗證縱向擴容的效果,本章我們來驗證橫向擴容對吞吐量的影響; 本文地址:http://blog.

Junit學習筆記測試驅動開發

1.正常的開發流程 編碼--->測試--->重複--->提交 基於測試驅動的開發 測試--->編碼--->重複--->提交 先寫了測試之後,由於測試的覆蓋率要求為100%,所以就會讓程式碼中可能存在的分支都進行測試,這樣先寫測試單元,可以

Hadoop學習筆記用MRUnit做單元測試

引言 借年底盛宴品鑑之風,繼續抒我Hadoop之情,本篇文章介紹如何對Hadoop的MapReduce進行單元測試。MapReduce的開發週期差不多是這樣:編寫mapper和reducer、編譯、打包、提交作業和結果檢索等,這個過程比較繁瑣,一旦提交到分散式環境出了問題要定位除錯,重複這樣的過程實在無趣

自動化測試系列使用Katalon Recorder自動生成基於Selenium框架的測試程式碼

系列連結 介紹 上一節說到有人提議用Selenium IDE,本人親自試用了一下,發現不太好用,一是因為它不能export出程式碼,二是輸入的引數是寫死的,比如現在我輸入的是hello world,可能我下次使用的時候,需要輸入ABCD, 那是不是需要手工的去

VCSA 6.5 HA配置 準備工作

vmware vcenter ha 高可用 vcsa 接著上一篇文章部署完成VCSA 6.5後,還需要做一些準備工作才能開啟高可用功能,本篇文章主要就講述如何為vCenter 高可用進行準備工作配置vCenter HA網絡從vCenter HA的架構圖中可以看出對於vCenter HA的高

Linux學習文件夾系統的結構和相對(絕對)路徑

sharp 二進制 沒有 數據 csharp pan 用戶 ont 臨時 理解每個目錄的作用 bin   二進制文件 boot   系統的啟動文件、內核 dev   設備文件 etc   配置文件 home  用戶的家目錄 lib    鏈接庫文件  l

RabbitMQ系列教程發布/訂閱(Publish/Subscribe)

mqc 標題 整合 參數 cti 事情 return 控制臺 run (本教程是使用Net客戶端,也就是針對微軟技術平臺的) 在前一個教程中,我們創建了一個工作隊列。工作隊列背後的假設是每個任務會被交付給一個【工人】。在這一部分我們將做一些完全不同的事情--我們將向多個

OSPF詳解OSPF LSA詳解

ospf lsa詳解 forwarding address OSPF LSA詳解OSPF V2版本中常用的主要有6類LSA,分別是Router-LSA、Network-LSA、Network-summary-LSA、ASBR-summary-LSA、AS-External-LSA、NSSA-LSA,接

Django運維後臺的搭建用url去精細定制與反向解析

django 反向解析 參數傳遞 url指向 上一篇文章裏,我們做了一個alionlineecs(阿裏雲線上環境服務器)的添加界面,但是要知道我們的計劃裏是有六個分支的,而alionlineecs僅僅是其中之一,要是每一個都這麽寫的話,那麽views.py肯定又臭又長,充滿了大量的復制片段。對

camera攝像原理色溫和自動白平衡【轉】

mil gho 實現 技術分享 處理 目標 紅旗 適應 如果 轉自:http://blog.csdn.net/ghostyu/article/details/7912863 色溫的定義:將黑體從絕對零度開始加溫,溫度每升高一度稱為1開氏度(用字母K表示),當溫度升高到一定

Halcon學習有關圖像通道的函數

spa com detail too pan targe 個數 word pop 黑白攝像機會返回每個像素所對應的能量采用結果,這些結果組成了一幅單通道灰度值圖像,而對於RGB彩色攝像機,它將返回每個像素所對應的三個采樣結果,也就是一幅三通道圖像。下面這些是與圖像通道有關的

SoC嵌入式軟件架構設計代碼分塊(Bank)設計原則

post 介紹 讀寫 cor 層次 clas rom bank 分配 上一節講述了在沒有MMU的CPU(如80251、MIPS M控制器系列、ARM cortex m系列)上實現虛擬內存管理的集成硬件設計方法。新設計的內存管理管理單元要實現虛擬內存管理還須要

初識Redis系列Redis支持的數據類型及使用

ted print 數據類型 eight 排序 sorted ring hang 無序 支持的數據類型有五種: string(字符串)、hash(哈希)、list(列表)、set(集合)及zset(sorted set:有序集合); 下面分別對這幾種類型進行簡單的Redis

緩存系列redis安裝及基本數據類型命令使用

pytho children tile 指令 sed eject 檢測 install 文件的 一:Redis是一個開源的key-value存儲系統。與Memcached類似,Redis將大部分數據存儲在內存中,支持的數據類型包括:字符串、哈希表、鏈表、集合、有序集合以及基

SIPp常用腳本UAC

pause rep iso sof app peer ati test level UAC是作為SIP消息的發起端,可以控制消息速率什麽的,方便極了。 一、uac.xml <?xml version="1.0" encoding="ISO-8859-1" ?>

Openfire分析ConnectionManager 連接管理(1)

max .com exc tco active eat cond hlist 觀察 Openfire是怎麽實現連接請求的?   XMPPServer.start()方法,完成Openfire的啟動。但是,XMPPServer.start()方法中,並沒有提及如何監聽端口,

Dapper系列Dapper的修改與刪除

幫助 一個 。。 講解 復制 upd font 希望 update Dapepr的Update和Delete 上兩篇文章我們介紹Dapper中添加和查詢。本篇文章我們繼續講解修改和刪除。。。。。如果本篇文章看不懂,請看閱讀上兩篇Dapper系列相關文章Update

Hadoop學習------Hadoop安裝方式()分布式部署

之間 root用戶 jar .sh author tables eth1 report 標識 這裏為了方便直接將單機部署過的虛擬機直接克隆,當然也可以不這樣做,一個個手工部署。 創建完整克隆——>下一步——>安裝位置。等待一段時間即可。 我這邊用了三臺虛擬