성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 그냥 RSS Reader 기능과 약간의 UI 편의성 때문에 사용...
[이종효] 오래된 소프트웨어는 보안 위협이 되기도 합니다. 혹시 어떤 기능...
[정성태] @Keystroke IEEE의 문서를 소개해 주시다니... +_...
[손민수 (Keystroke)] 괜히 듀얼채널 구성할 때 한번에 같은 제품 사라고 하는 것이 아...
[정성태] 전각(Full-width)/반각(Half-width) 기능을 토...
[정성태] Vector에 대한 내용은 없습니다. Vector가 닷넷 BCL...
[orion] 글 읽고 찾아보니 디자인 타임에는 InitializeCompon...
[orion] 연휴 전에 재현 프로젝트 올리자 생각해 놓고 여의치 않아서 못 ...
[정성태] 아래의 글에 정리했으니 참고하세요. C# - Typed D...
[정성태] 간단한 재현 프로젝트라도 있을까요? 저런 식으로 설명만 해...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# - Oracle.ManagedDataAccess의 Pool 및 그것의 연결 개체 수를 알아내는 방법</h1> <p> 이것을 알아내는 방법은, 공식적으로는 성능 카운터를 이용하는 방법이 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - (.NET Framework를 위한) Oracle.ManagedDataAccess 패키지의 성능 카운터 설정 방법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13003'>https://www.sysnet.pe.kr/2/0/13003</a> C# - Oracle.ManagedDataAccess.Core의 성능 카운터 설정 방법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13000'>https://www.sysnet.pe.kr/2/0/13000</a> </pre> <br /> 하지만, 아쉽게도 좀 번거롭다는 단점이 있습니다. 그냥 저런 설정 없이 알아낼 수 있다면 좋을 텐데요, 다행히 리플렉션을 이용하면 그런대로 원하는 목적을 이룰 수 있습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그럼, 어디서 시작해볼까요? ^^<br /> <br /> 우선, OracleConnection.ClearAllPools 메서드가 있는데요, <br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public static void ClearAllPools() { // ...[생략]... OracleConnectionDispenser<OraclePoolManager, OraclePool, OracleConnectionImpl>.<span style='color: blue; font-weight: bold'>ClearAllPools()</span>; // ...[생략]... } </pre> <br /> 여기서 다시 OracleConnectionDispenser`3.ClearAllPools를 들어가면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > internal static void ClearAllPools() { // ...[생략]... <span style='color: blue; font-weight: bold'>List<PM> list = OracleConnectionDispenser<PM, CP, PR>.m_listPM.GetList(); for (int index = 0; index < list.Count; ++index) list[index].ClearAllPools(default (PR), false); }</span> // ...[생략]... } </pre> <br /> OracleConnectionDispenser`3 제네릭 클래스의 m_listPM 정적 필드에 Pool 개체들이 담겨 있음을 짐작게 합니다. 해당 제네릭 타입은 3개의 타입 인자를 받아들이므로,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > OracleConnectionDispenser<OraclePoolManager, OraclePool, OracleConnectionImpl> </pre> <br /> <a target='tab' href='https://www.sysnet.pe.kr/2/0/13005'>이전에 설명한 방법</a>을 사용해 일단 이렇게 m_listPM 목록을 구할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using Oracle.ManagedDataAccess.Client; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; internal class Program { static void Main(string[] args) { Assembly asm = typeof(OracleConnection).Assembly; Dictionary<string, Type> typeArgs = new Dictionary<string, Type>() { ["OracleInternal.ConnectionPool.OraclePoolManager"] = null, ["OracleInternal.ConnectionPool.OraclePool"] = null, ["OracleInternal.ServiceObjects.OracleConnectionImpl"] = null, }; Type poolType = null; foreach (Type type in asm.GetTypes()) { string name = type.FullName; if (typeArgs.ContainsKey(type.FullName) == true) { typeArgs[type.FullName] = type; continue; } if (name.IndexOf("OracleConnectionDispenser") == -1) { continue; } poolType = type; } bool resolvedAll = typeArgs.All((e) => e.Value != null); if (resolvedAll == false) { return; } Type[] genericArgs = new Type[] { typeArgs["OracleInternal.ConnectionPool.OraclePoolManager"], typeArgs["OracleInternal.ConnectionPool.OraclePool"], typeArgs["OracleInternal.ServiceObjects.OracleConnectionImpl"] }; Type poolGenericType = poolType.MakeGenericType(genericArgs); { FieldInfo fi = poolGenericType.GetField("m_listPM", BindingFlags.Static | BindingFlags.Public); object syncQList = fi.GetValue(null); Console.WriteLine(syncQList); } } } </pre> <br /> 그다음, syncQList에는 개체 풀을 유지하는 목록이 들어 있을 텐데요, 해당 타입을 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System.Collections.Generic; namespace OracleInternal.Common { internal class SyncQueueList<T> { <span style='color: blue; font-weight: bold'>internal List<T> m_list;</span> internal object m_sync; internal int m_max; internal bool m_bMaxExplicitlySet; // ...[생략]... } } </pre> <br /> 다시 내부의 리스트로 보관하고 있습니다. 어쩔 수 없이 그 필드를 구하는,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > { FieldInfo fi = poolGenericType.GetField("m_listPM", BindingFlags.Static | BindingFlags.Public); object syncQList = fi.GetValue(null); <span style='color: blue; font-weight: bold'>object poolList = GetInstanceFieldValue(syncQList, "m_list", BindingFlags.NonPublic | BindingFlags.Instance);</span> int numberOfPool = 0; foreach (var pool in poolList as IEnumerable) { numberOfPool++; } Console.WriteLine(numberOfPool); } private static object GetInstanceFieldValue(object objInstance, string fieldName, BindingFlags flags) { return objInstance.GetType().GetField(fieldName, flags).GetValue(objInstance); } </pre> <br /> 위의 코드를 실행하면, 현재 풀이 만들어진 적이 없으므로 numberOfPool 값은 0이 나옵니다. 이 상태에서 다음의 예제 코드를 한번 실행시켜 두면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > string oradb = "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=127.0.0.1)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=XE)));User Id=hr;Password=hr2;"; DoOracleManagedCall(oradb, 0); protected static void DoOracleManagedCall(string oradb, int delay) { using (OracleConnection oraConnection = new OracleConnection(oradb)) { oraConnection.Open(); if (delay > 0) { Thread.Sleep(delay); } } } </pre> <br /> 이제 리플렉션 코드는 numberOfPool의 개수로 1을 반환합니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 자, 그럼 저 m_list는 OraclePoolManager의 목록을 가지고 있게 되는데요, (OraclePoolManager가 상속받은) PoolManager.ClearAllPools의 메서드를 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public virtual void ClearAllPools(PR pr, bool isHAEvnt = false) { // ...[생략]... List<CP> list = <span style='color: blue; font-weight: bold'>this.m_pmListCP.GetList()</span>; for (int index = 0; index < list.Count; ++index) list[index].<span style='color: blue; font-weight: bold'>ClearPool</span>(pr, isHAEvnt); } // ...[생략]... } System.Collections.Generic.List`1[OracleInternal.ConnectionPool.OraclePoolManager] m_list </pre> <br /> 다시, 내부에서 구성한 List를 열거하면서 ClearPool 메서드를 호출하고 있습니다. 여기서 잘 이해가 안 됩니다. 일반적으로 다중 Pool이 제공되고 각각의 Pool에서 다시 연결 개체를 소유하는 것으로 알고 있었는데, Pool 목록에서 다시 개별 요소들이 Pool을 가지고 있는 유형으로 나옵니다.<br /> <br /> 어쨌든, m_pmListCP도 SyncQueueList`1 타입이므로 최종적으로 다음과 같이 열거할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // ...[생략]... int numberOfPool = 0; foreach (var pool in poolList as IEnumerable) { numberOfPool++; object pmListCP = GetInstanceFieldValue(pool, "m_pmListCP", BindingFlags.Public | BindingFlags.Instance); object pmListCPList = GetInstanceFieldValue(pmListCP, "m_list", BindingFlags.NonPublic | BindingFlags.Instance); foreach (var pmCP in pmListCPList as IEnumerable) { Console.WriteLine(pmCP); // OracleInternal.ConnectionPool.OraclePool } } Console.WriteLine(numberOfPool); </pre> <br /> ClearAllPools 메서드에서는 결국 저렇게 얻은 OracleInternal.ConnectionPool.OraclePool 개체의 ClearPool 메서드를 호출하는 건데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public void ClearPool(PR prToRetain, bool isHAEvnt = false) { // ...[생략]... List<PR> list = this.<span style='color: blue; font-weight: bold'>m_cpListPR</span>.GetList(); for (int index = 0; index < list.Count; ++index) { // ...[생략]... PR pr = list[index]; if ((object) pr != null) { // ...[생략]... pr.m_pm.<span style='color: blue; font-weight: bold'>Close</span>(pr, (OracleConnection) null); // ...[생략]... } // ...[생략]... } // ...[생략]... } </pre> <br /> 보는 바와 같이 m_cpListPR이라는 목록을 연결 개체가 보관하는 듯하고, Close 메서드를 호출함으로써 연결을 정리하고 있습니다. 그래서 대략 다음과 같은 식으로 코드를 완성할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <span style='color: blue; font-weight: bold'>int numberOfPool = 0; int numberOfCount = 0;</span> foreach (var pool in poolList as IEnumerable) { <span style='color: blue; font-weight: bold'>numberOfPool</span>++; object pmListCP = GetInstanceFieldValue(pool, "m_pmListCP", BindingFlags.Public | BindingFlags.Instance); object pmListCPList = GetInstanceFieldValue(pmListCP, "m_list", BindingFlags.NonPublic | BindingFlags.Instance); foreach (var pmCP in pmListCPList as IEnumerable) { // OracleInternal.Common.SyncQueueList`1[OracleInternal.ServiceObjects.OracleConnectionImpl] object cpListPR = GetInstanceFieldValue(pmCP, "m_cpListPR", BindingFlags.Public | BindingFlags.Instance); object cpListPRList = GetInstanceFieldValue(cpListPR, "m_list", BindingFlags.NonPublic | BindingFlags.Instance); int cpListSize = (int)GetInstanceFieldValue(cpListPRList, "_size", BindingFlags.NonPublic | BindingFlags.Instance); <span style='color: blue; font-weight: bold'>numberOfCount</span> += cpListSize; } } <span style='color: blue; font-weight: bold'>Console.WriteLine($"# of pools: {numberOfPool}"); Console.WriteLine($"# of connections: {numberOfCount}");</span> </pre> <br /> <hr style='width: 50%' /><br /> <br /> 실제로 저 코드를 WriteCount라는 메서드로 분리해서 다음과 같이 실행해 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > string oradb1 = "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=127.0.0.1)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=XE)));User Id=hr;Password=hr2;Min Pool Size=15;"; string oradb2 = "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=127.0.0.1)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=XE)));User Id=hr;Password=hr2;Min Pool Size=10;"; string oradb3 = "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=127.0.0.1)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=XE)));User Id=hr;Password=hr2;"; do { Type poolGenericType = poolType.MakeGenericType(genericArgs); WriteCount(poolGenericType); for (int i = 0; i < 17; i++) { DoOracleManagedCall(oradb1, 1000); } WriteCount(poolGenericType); Thread.Sleep(1000); for (int i = 0; i < 17; i++) { DoOracleManagedCall(oradb2, 1000); } WriteCount(poolGenericType); Thread.Sleep(1000); for (int i = 0; i < 17; i++) { DoOracleManagedCall(oradb3, 1000); } WriteCount(poolGenericType); Console.WriteLine("---------------------------------------------------------------"); } while (true); </pre> <br /> 서로 다른 연결 개체를 3개 사용했으므로, 3개의 연결 개체 풀이 생성될 것이고, 각각의 풀에는 17개의 연결 개체가 들어 있을 것입니다. 운이 좋은 경우 정확히 3 * 17 = 51개가 나오는 경우도 있지만, 대개의 경우 풀 내부의 정책으로 더 있을 수 있습니다.<br /> <br /> 실제로 이를 성능 모니터링 도구와 함께 확인해 보면 다음과 같이 나옵니다.<br /> <br /> <img alt='oracle_pool_status_1.png' src='/SysWebRes/bbs/oracle_pool_status_1.png' /><br /> <br /> 보시면, 3개의 풀이 있고, 각각의 풀에는 21개, 17개, 17개까지 총 55개의 연결 개체가 생성돼 있는 것을 볼 수 있습니다. 그리고 그 값은 정확하게 성능 모니터링 도구의 "NumberOfPooledConnections" 수와 일치합니다.<br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1912&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1782
(왼쪽의 숫자를 입력해야 합니다.)