「.NET」と「ASP.NET」からのSQL Serverへのコネクションを確認する(2)

「.NET」と「ASP.NET」からの接続には大きな違いがあります。
それは普通の?(exe形式の)アプリケーションか、Webアプリケーションかです。

この違いはアプリの終了というタイミングに大きく影響してきます。
「.NET」の場合は、例えどれだけコネクションがあろうとも、exeが終了した段階でコネクションはCloseされます。
更に正確にいうと、exeが終了した段階で、Close処理を実施していないコネクションも、ガベージコレクションによりCloseされます。

定期的に実行されるexeでは、もはやclose処理を意識しなくてもいいかもしれません。
このことは下記リンクのプログラムで確認することができます。

パフォーマンス カウンター - ADO.NET
ADO.NET パフォーマンス カウンターを使用すると、Windows パフォーマンス モニターやプログラムを使用して、アプリケーションの状態やその接続リソースを監視できます。

ポイントは2つ。app.configと、コメントアウトです。

肝心なNumberOfPooledConnectionsを確認するには下記設定が必用です。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
    </startup>
    <system.diagnostics>
      <switches>
        <add name="ConnectionPoolPerformanceCounterDetail" value="4"/>
      </switches>
    </system.diagnostics>

</configuration>

またサンプルでは、重要な個所がコメントアウトされているので注意しましょう。

    Private Enum ADO_Net_Performance_Counters
        NumberOfActiveConnectionPools
        NumberOfReclaimedConnections
        HardConnectsPerSecond
        HardDisconnectsPerSecond
        NumberOfActiveConnectionPoolGroups
        NumberOfInactiveConnectionPoolGroups
        NumberOfInactiveConnectionPools
        NumberOfNonPooledConnections
        NumberOfPooledConnections
        NumberOfStasisConnections
        ' The following performance counters are more expensive to track.
        ' Enable ConnectionPoolPerformanceCounterDetail in your config file.
        '     SoftConnectsPerSecond
        '     SoftDisconnectsPerSecond
        '     NumberOfActiveConnections
        '     NumberOfFreeConnections
    End Enum

多くの人の感心は「ASP.NET」でのコネクションと解放でしょう。
同じプログラムを流用して、.NETのコネクションも確認することができます。

私は下記サンプルを使用して何度も試行しました。
Default.aspx

Imports System.Data.SqlClient
Imports System.Diagnostics
Imports System.Runtime.InteropServices


Partial Class _Default
    Inherits System.Web.UI.Page


    Private con As New SqlConnection
    Private PerfCounters(13) As PerformanceCounter
    Private connection As SqlConnection = New SqlConnection

    Private Enum ADO_Net_Performance_Counters
        NumberOfActiveConnectionPools
        NumberOfReclaimedConnections
        HardConnectsPerSecond
        HardDisconnectsPerSecond
        NumberOfActiveConnectionPoolGroups
        NumberOfInactiveConnectionPoolGroups
        NumberOfInactiveConnectionPools
        NumberOfNonPooledConnections
        NumberOfPooledConnections
        NumberOfStasisConnections
        ' The following performance counters are more expensive to track.
        ' Enable ConnectionPoolPerformanceCounterDetail in your config file.
        SoftConnectsPerSecond
        SoftDisconnectsPerSecond
        NumberOfActiveConnections
        NumberOfFreeConnections
    End Enum

    Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load

        con.ConnectionString = "Data Source=localhost;User Id=user;Password=pass;Initial Catalog=TestDB;Pooling=true;Min Pool Size=0;Max Pool Size=10;Connection Lifetime=1"
        con.Open()
        System.Threading.Thread.Sleep(5000)
        con.Close()

        Me.PerfCounters(13) = New PerformanceCounter()

        Dim instanceName As String = GetInstanceName()
        Dim apc As Type = GetType(ADO_Net_Performance_Counters)
        Dim i As Integer = 0
        Dim s As String = ""
        For Each s In [Enum].GetNames(apc)
            Me.PerfCounters(i) = New PerformanceCounter()
            Me.PerfCounters(i).CategoryName = ".NET Data Provider for SqlServer"
            Me.PerfCounters(i).CounterName = s
            Me.PerfCounters(i).InstanceName = instanceName
            i = (i + 1)
        Next


        Response.Write("---------------------------" & "<br/>")
        For Each p As PerformanceCounter In Me.PerfCounters
            Response.Write(p.CounterName & ":" & p.NextValue & "<br/>")
        Next
        Response.Write("---------------------------" & "<br/>")

    End Sub

    Private Declare Function GetCurrentProcessId Lib "kernel32.dll" () As Integer
    Private Function GetInstanceName() As String
        'This works for Winforms apps. 
        ''Dim instanceName As String = System.Reflection.Assembly.GetEntryAssembly.GetName.Name
        '''Dim instanceName As String = System.Web.Hosting.HostingEnvironment.ApplicationID
        ' Must replace special characters like (, ), #, /, \\ 
        Dim instanceName2 As String = AppDomain.CurrentDomain.FriendlyName.ToString.Replace("(", "[").Replace(")", "]").Replace("#", "_").Replace("/", "_").Replace("\\", "_")

        'For ASP.NET applications your instanceName will be your CurrentDomain's 
        'FriendlyName. Replace the line above that sets the instanceName with this: 
        Dim instanceName As String = AppDomain.CurrentDomain.FriendlyName.ToString.Replace("(", "[") _
            .Replace(")", "]").Replace("#", "_").Replace("/", "_").Replace("\\", "_")

        Dim pid As String = GetCurrentProcessId.ToString
        instanceName = (instanceName + ("[" & (pid & "]")))
        Console.WriteLine("Instance Name: {0}", instanceName)
        Console.WriteLine("---------------------------")
        Return instanceName
    End Function



End Class

もちろんapp.configの代わりにweb.configに以下の追加を行います。
web.config

  <system.codedom>
・・・・
  </system.codedom>
  <system.diagnostics>
  <switches>
    <add name="ConnectionPoolPerformanceCounterDetail"
         value="4"/>
  </switches>
  </system.diagnostics>
</configuration>

このようなクライアントからのアクセスと、SQLSERVERでのコネクション状況を確認すると見えてくるのが、
コネクションはクライアントが管理しているということです。

SQLSERVER側で

select hostname,count(*) from master..sysprocesses where hostname!='' group by hostname

というコマンドで接続状況を監視してみてください。

WEBサーバー側のweb.configを変更しなくて結構ですので上書きしてみてください。
もし、Closeされていなくて待ちとなっていたコネクションがあればCloseされることが確認できるはずです。

SQLSERVER側ではClose処理をしたコネクションなのか、まだ使用しているコネクションなのかが明確にはわかりません。
それはSQLSERVER側からみたら、どちらも使用中のコネクションだからです。
コネクションプールという概念は、実はクライアント側からみたコネクションの概念なのです。
再利用するためにプールに保存してるか、まだ使用中なのかはクライアントからみた概念なのです。

何にせよCloseは重要です。
Closeせずに再度接続をすればコネクションは増えます。
もしCloseしたコネクションがあれば再利用します。

Webアプリケーションでは、exeのように明確なアプリケーションの終了というタイミングがありません。
Close処理は、文字通りのClose処理ではなく、再利用可能であるという信号をおくるだけになります。
この点を注意しなくては、IISによる大規模なシステムは構築できません。

補足事項としてもう一点、コネクションはDataReaderにも発します。
コネクションから取得したデータをDataReaderに持たせるとします。
すると2つのコネクションが発生します。

リソースは閉じるという基本が重要です。
リソースはusingを使用すべし!ともいえるかもしれません。

タイトルとURLをコピーしました