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
元件例項化上下文,包含許多實現,僅列舉其中的ConstantCallSite
,CreateInstanceCallSite
,ConstructorCallSite
ConstructorCallSite
既是IServiceCallSite
的實現類,又複合引用IServiceCallSite
,以表達自身引數的構建形式。
元件生命週期也使用IServiceCallSite
表達,它們既從IServiceCallSite
繼承,也引用IServiceCallSite
。
CallSiteFactory
當元件需要被例項化時,CallSiteFactory
從維護的ServiceDescriptor
查詢注入方式,對型別注入的元件使用反射解析其建構函式,並遞迴解析其引數,最後快取得到的IServiceCallSite
例項。
ServiceProviderEngine
ServiceProviderEngine
是抽象類,內部依賴CallSiteRuntimeResolver
完成基於反射的元件例項化,並快取了元件例項化的委託。
CompiledServiceProviderEngine
CompiledServiceProviderEngine
從ServiceProviderEngine
繼承,內部依賴ExpressionResolverBuilder
完成基於表示式樹的元件例項化的委託。
DynamicServiceProviderEngine
DynamicServiceProviderEngine
從CompiledServiceProviderEngine
繼承,它建立的委託比較特殊:
- 該委託第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
的原樣繼承可以忽略,ExpressionsServiceProviderEngine
和CompiledServiceProviderEngine
都是表示式樹的使用也沒有差異。ILEmitServiceProviderEngine
是 emit 相關的實現。
從aspnet/DependencyInjection 獲取到的分支release/2.1 後,提取到ServiceProviderEngine
、CompiledServiceProviderEngine
和ILEmitServiceProviderEngine
等核心實現,再編寫控制檯程式,對依賴注入中反射、表示式樹、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 標識 這裏為了方便直接將單機部署過的虛擬機直接克隆,當然也可以不這樣做,一個個手工部署。 創建完整克隆——>下一步——>安裝位置。等待一段時間即可。 我這邊用了三臺虛擬