いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

WCF入門-005 (基本的なサンプル, 構成ファイルを利用, MSMQ, バインディングにMsmqBinding(メッセージキュー)を利用)


前回では、バインディングにNetNamedPipeBindingを利用していましたが
今回は、メッセージキュー(NetMsmqBinding)を利用してみます。


メッセージキューを利用するバインディングですので
当然ながら、MSMQが動作していないと動きません。
Windows Server 2008 R2などの場合は、サーバーマネージャから
「機能の追加」でインストールできます。


NetMsmqBindingは、文字通りMSMQ (メッセージキュー)をトランスポートとして
利用するバインディングです。このバインディングは他のバインディングとは
ちょっと違う点があります。


今までのバインディングの場合、クライアントとサーバーが直接
やり取りをしていましたが、MSMQバインディングを利用する場合、
クライアントとサーバーの間にメッセージキューが仲介として入ります。


イメージとしては以下のような感じ。

クライアント ⇔ MSMQ ⇔ サービスホスト

クライアントから送信したデータは、メッセージキューにenqueueされ
サービスホストは、メッセージキューからデータをdequeueします。


なので、NetMsmqBindingを利用している場合、クライアントは
MSMQが動いていれば、サービスホストが起動していなくても動きます。
(メッセージキューにデータがたまっていきます。)


で、サービスホストは起動したらメッセージキューに溜まっている
データを取得し、処理が流れていきます。


メッセージキューを利用する場合は、以下の組み合わせを利用します。

NetMsmqBindingを利用する場合、アドレスはキューの場所を示すように設定します。
(例)net.msmq://localhost/private/testqueue


以下、サンプルです。
まずサービス定義.

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;

namespace Gsf.Samples.WCF
{
    [ServiceContract(Namespace="http://Gsf.Samples.WCF")]
    public interface IMyService
    {
        [OperationContract(IsOneWay=true)]
        void SendData(int value);
    }
}
using System;
using System.Collections.Generic;
using System.Linq;

namespace Gsf.Samples.WCF
{
    public class MyService : IMyService
    {
        public void SendData(int value)
        {
            Console.WriteLine("[CALL] SendData() : {0}", value);
        }
    }
}


次にホストアプリ。

ホストアプリでは、最初にメッセージキューに利用するキュー名が存在するか
どうかを確認しています。無ければ作成しておきます。

尚、メッセージキューを操作する場合は、System.Messagingの参照設定が必要です。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Messaging;
using System.ServiceModel;

namespace Gsf.Samples.WCF
{
    class Program
    {
        const string TX_QUEUE_NAME = @".\private$\testqueue";

        static void Main(string[] args)
        {
            //
            // キューにサンプルで利用する名前が存在するか確認.
            // なければ、ここで作成しておく.
            //
            if (!MessageQueue.Exists(TX_QUEUE_NAME))
            {
                MessageQueue.Create(TX_QUEUE_NAME, true);
            }

            using (var host = new ServiceHost(typeof(MyService)))
            {
                host.Open();

                Console.WriteLine("サービスが開始されました。");
                Console.WriteLine("press <ENTER> to exit...");
                Console.ReadLine();

                host.Close();
            }
        }
    }
}


ホスト側のアプリケーション構成ファイル。
アドレスの部分にて、メッセージキュー(NetMsmqBinding)を指定しています。
尚、私の環境ではActive Directory統合を無効化した状態でインストールしていますので
bindingの設定にてsecurityモードをNoneに設定しています。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="Gsf.Samples.WCF.MyService">
        <host>
          <baseAddresses>
            <add baseAddress="net.msmq://localhost/private/testqueue"/>
          </baseAddresses>
        </host>
        <endpoint address="" binding="netMsmqBinding" contract="Gsf.Samples.WCF.IMyService" bindingConfiguration="NoMsmqSecurity" />
      </service>
    </services>
    <bindings>
      <netMsmqBinding>
        <binding name="NoMsmqSecurity">
          <security mode="None" />
        </binding>
      </netMsmqBinding>
    </bindings>
  </system.serviceModel>
</configuration>


次にクライアント側。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace Gsf.Samples.WCF
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btnCallServiceMethod_Click(object sender, EventArgs e)
        {
            using (var client = new MyServiceRef.MyServiceClient())
            {
                var rnd = new Random();
                client.SendData(rnd.Next());

                client.Close();
            }
        }
    }
}


最後にクライアント側のアプリケーション構成ファイル。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>        
      <client>
        <endpoint address="net.msmq://localhost/private/testqueue" 
                  binding="netMsmqBinding" 
                  contract="MyServiceRef.IMyService"
                  bindingConfiguration="NoMsmqSecurity"/>
      </client>
      <bindings>
        <netMsmqBinding>
          <binding name="NoMsmqSecurity">
            <security mode="None" />
          </binding>
        </netMsmqBinding>
      </bindings>
    </system.serviceModel>
</configuration>


実行すると以下のようになります。


クライアント側からサービスメソッドを呼び出すといつも通り動きます。
その後、ホストアプリ側だけを落として、クライアントの「サービス呼び出し」
ボタンを何回かクリックします。ホスト側は落ちているのに正常に動作します。


メッセージキューを見てみると、ホストアプリを落としてから、クライアントが
送ったデータがキューに残っているのがわかります。


クライアントを落として、今度はホストアプリのみを起動すると
先ほどキューに溜まっていたデータが処理されます。



サンプルは以下の場所にアップしてあります。
https://sites.google.com/site/gsfzero1/Home/WCF-Sample-005.zip?attredirects=0&d=1


================================
過去の記事については、以下のページからご参照下さい。