QA@IT

XML Webサービスでのコネクション解放

4449 PV

お世話になります。
Visual Studio 2008を使用して、XML WebサービスとWebサービスクライアントアプリケーションを作成する練習をしております。

さて、表題のコネクション解放についてですが、
Management Studioの利用状況モニタ等を使って観察してみると、どうやら自分が思ったタイミングでDBとのコネクションが解放されていないようです。

自分の記述方法が悪いのか、それともコネクションプーリングの関係等でしばらくコネクションが残っているだけなのか、お教え頂きたく、投稿させて頂きました。

サーバ環境は以下の通りです。
Windows Server 2008 SP2
Microsoft SQL Server 2008(SP1)
Internet Information Service 7.0
VB.NET

サーバ側記述

<WebMethod()> _
Public Function GetData() As String
     Dim con As New SqlConnection
     Dim cmd As NEW SqlCommand
     Dim dr As SqlDataReader
     Dim mysql As String
     Dim ret as String


     con.ConnectionString="----省略----"
     con.Open()
     cmd.Connection=con

     mysql="----何かのSQL----"
     cmd.CommandText=mysql
     cmd.CommandType=CommandType.Text
     dr=cmd.Executereader

     ''''''''''''''''''''''''''''''''
     'クエリの結果からretに何かを格納
     ''''''''''''''''''''''''''''''''

     dr.Close()
     con.Close()←ここで解放されない?
     cmd.Dispose()
     con.Dispose()
     Return ret
End Function

クライアント側記述

Private Sub Test() As String
     '上記サービス
     Dim myservice As New Project.WebReference.Class 
     Dim result as String

     result=myservice.GetData()

End Sub

追記
クライアント側からGetData()を数秒おきに連続して呼び出すと、利用状況モニタで該当するDBに接続するプロセスが呼び出した回数分、増えている状態です。
サーバ側でcon.Close()した時点でプールに接続が返されていて、再度GetData()を呼び出した際は、プールから空いている接続を取得すると考えていたのですが、どうやら違うようだと思い、質問させて頂いております。
自分の記述が悪く、プールへと接続を返し切れていないのか、それ以外の原因があるのか、ご教示下さい。

以上です。
よろしくお願い致します。

後日談
SurferOnWww様にお教え頂いた方針の通りに全てのコードをリファクタリングした所、自分が気になっていた現象は、解消されたようです。
ありがとうございました。

  • 以下「追記」へのレスです。コード的には、Close の前に例外がスローされて終わってしまわない限り、コネクションリークの問題は起きないはずです。利用状況モニタで見たとのことですが、コネクションプールとは関係ないものを見ているのではないですか? -
  • 接続状況は「パフォーマンスカウンター (ADO.NET)」で見ると分かると思います。
    http://msdn.microsoft.com/ja-jp/library/vstudio/ms254503(v=vs.100).aspx
    -
  • 回答に追記しました。 -

回答

Management Studioの利用状況モニタ等を使って観察してみると、どうやら自分が思った
タイミングでDBとのコネクションが解放されていないようです。

何をどうやって見たのか分かりませんが、Open/Close で物理的なデータベース接続がそのつどオープンされたりクローズされたりすることはなくて、Open はコネクションプールからのコネクションの貸し出し、Close はプールへの返却処理になっているという理由だと思います。

以下のブログの Part.2 の方にそのあたりの記述や、そんなことよりもっと大切なこととして、コネクションリークをどう防止するかについて詳しく書いてあります。

このブログに書いてあるように、コネクションリーク防止の対策がきちんと取られていれば、上記の話は気にしなくていいと思います。

.NETの例外処理 Part.1
http://blogs.msdn.com/b/nakama/archive/2008/12/29/net-part-1.aspx

.NETの例外処理 Part.2
http://blogs.msdn.com/b/nakama/archive/2009/01/02/net-part-2.aspx

コネクションリーク防止は、例外処置と深い関係がありますので、Part.1 から読むことをお勧めします。個人的には例外処置に関するバイブル的な情報だと思います。

編集 履歴 (2)
  • 例外処理の内容、じっくりと読ませて頂きました。
    自分がtry~catchをつかって、都度後処理をしていたこともあり、非常に勉強になりました。
    ただ、上記のコードは例外が起こらなければ、お教え頂いた記事を元に考えてみましたが、リソースリークを起こしている部分は無い様に思います。
    質問内容に追記をしておりますので、宜しければもう少し質問を継続させて頂けないでしょうか。
    よろしくお願い致します。
    -
  • SurferOnWww様
    返答が遅くなり、申し訳ありません。
    自分自身も、ご掲示頂いた記事の内容を読んで、大変勉強になりましたので、一度キレイにリファクタリングしてみて、再度確認してみようと思います。ありがとうございました。
    -

こちらのMSDNが参考になると思います。

SQL Server の接続プール (ADO.NET)
http://msdn.microsoft.com/ja-jp/library/8xx3tyca(v=vs.110).aspx


接続が最初に開かれると、完全一致のアルゴリズムに基づいて接続プールが作成され、接続内の接続文字列に関連付けられます。 各接続プールが別の接続文字列に関連付けられます。


接続がプールに返されるようにするために、接続を使い終えたら必ず接続を終了することを強くお勧めします。 この操作は、Connection オブジェクトの Close または Dispose メソッドを使用するか、あるいは C# の using ステートメントまたは Visual Basic の Using ステートメント内ですべての接続を開くことによって実行できます。


追記

クライアント側からGetData()を数秒おきに連続して呼び出すと、利用状況モニタで該当するDBに接続するプロセスが呼び出した回数分、増えている状態です。

C#ですが XML Webサービスを作って実行してみましたが特にそういう事は起こりませんでした(ソースは後述しますがご提示のものと似た構成にしてあります)。
(rs.closeがなんだったのかわからなかったのでDataReader(dr)を閉じた場合、閉じなかった場合両方試しました。)
Win2008、SQLServer 2008 sp1、VS2013 (XML Webサービスはtarget frameworkを .NET 2.0で作成)。
※ 具体的には、2種類のWeb Methodを別の接続文字列で用意したので、利用状況モニタには、tempdbを参照する 1つと、各Web Methodの接続 1つずつの計 3つ以上になることはありませんでした。

利用状況モニタのプロセスタブのセッションも、パフォーマンスモニタのSQL Serverの接続数(User Connections)も増加しませんでした。
ただし先にあげたリンク先にもありますが接続文字列が異なったり、Windows認証で接続ユーザが異なる場合は別の接続が必要になりますので接続は増えます。
また接続が使用中であれば当然接続は増えますので、たとえば 30秒かかる処理を数秒おきに別のクライアントから呼び出せば再利用できない間は接続は当然増えていきます。
利用状況モニタでのログインや実行されているコマンドなども含めて見なおしてみるといいかもしれません。

[WebMethod]
public string GetFirst() {
    var connectionString = "Persist Security Info=False;User ID=user001;Password=user001;Initial Catalog=SimpleWebServer;Server=XXXXXXXXX";
    var conn = new SqlConnection(connectionString);
    conn.Open();

    var cmd = new SqlCommand();
    cmd.Connection = conn;

    var mysql = "select * from table_1 where id = 1";
    cmd.CommandText = mysql;
    cmd.CommandType = System.Data.CommandType.Text;

    var dr = cmd.ExecuteReader();

    dr.Read();
    var ret = dr[1].ToString();

    // dr.Close(); // rs が何か不明だった dataReader?
    cmd.Dispose();
    conn.Close();
    conn.Dispose();

    return ret;
}
編集 履歴 (2)
  • ありがとうございます。
    コネクションプーリングの考え方について、詳しい内容について確認することができました。
    ただ、宜しければもう少しだけ質問を継続させて下さい。
    内容については質問を追記しております。
    よろしくお願い致します。
    -
  • flied_onion様
    ご検証頂き、誠にありがとうございます。
    ご推察の通り、rsはdataReaderでした。申し訳ありません。
    了解致しました。再度細かく場合分けして、状況を観察してみようと思います。ありがとうございました。
    -
ウォッチ

この質問への回答やコメントをメールでお知らせします。