2011-10-20 11 views
18

私はWCFアプリケーションで少しパフォーマンスを上げ、ChannelsとChannelFactoryをキャッシュしようと決めました。私が始める前に私がクリアする必要があるこのすべてについて私が持っている2つの質問があります。WCFチャネルとChannelFactoryキャッシング

1)ChannelFactoryをシングルトンとして実装する必要がありますか?

2)私は個々のチャンネルをキャッシュ/再利用する方法についてよく分かりません。これを行う方法の例がありますか?

私のWCFサービスは、エンドポイントが1つのみのスタンドアロンアプリケーションとして展開されていることに注意することが重要です。

EDIT:

回答いただきありがとうございます。私はまだいくつかの質問があります...

1)私はキャッシングが起こるべき場所について混乱していると思います。私は、このコードを使用しているクライアントAPIを当社の別の部門に提供しています。このキャッシュはクライアント上で行われますか?

2)クライアントAPIはSilverlightアプリケーションの一部として使用されますが、これは何か変更されますか?特に、そのようなシナリオでどのようなキャッシングメカニズムが利用できるのですか?

3)私はGetChannelFactoryメソッドの設計についてまだ明確ではありません。サービスが1つしかない場合は、1つのChannelFactoryを作成してキャッシュする必要がありますか?

私はまだ(私はそれが行われるべきかについては全く困惑しているので!)任意のキャッシュ機能を実装しますが、ここで私はこれまで、クライアントプロキシの持っているものだしていない

namespace MyCompany.MyProject.Proxies 
{ 
    static readonly ChannelFactory<IMyService> channelFactory = 
     new ChannelFactory<IMyService>("IMyService"); 

    public Response DoSomething(Request request) 
    { 
     var channel = channelFactory.CreateChannel(); 

     try 
     { 
      Response response = channel.DoSomethingWithService(request); 
      ((ICommunicationObject)channel).Close(); 
      return response; 
     } 
     catch(Exception exception) 
     { 
      ((ICommenicationObject)channel).Abort(); 
     } 
    } 
} 
+0

#3の場合は、1つのチャネルファクトリだけを作成する必要があります。基本的には、お客様が保有するサービスエンドポイントごとに1つのチャネルファクトリを用意します。私の場合は、これまでに6層あり、主に2層に広がっています。あなたのケースでは、1つのアプリケーションだけを使用して1つのサービスしか持たない場合は、上でやっていることを単純に行うことができます。上記のコードは正しい軌道にあります。キャッシングはアプリのニーズに基づいて行われます。 – Tim

+0

@Tim - あなたのご協力のすべてに感謝します。私は本当にそれを感謝します!私のケースでは、私のサービスがとてもシンプルであったという事実が、複数のエンドポイントが存在するこれらの他の例を見て混乱を引き起こしていたと思います。私は今=あまり混乱していない=ティムは素晴らしい仕事を説明した!ありがとう、相棒! – Didaxis

+0

あなたは大歓迎です。私は助けてくれるとうれしいです - 幸せなコーディング! – Tim

答えて

20

使用ChannelFactoryを使用してファクトリのインスタンスを作成し、そのインスタンスをキャッシュします。必要に応じて、キャッシュされたアイチューンスからコミュニケーションチャネルを作成することができます。

複数のチャネルファクトリ(複数のサービスがありますか)が必要ですか?私の経験では、これがパフォーマンスの最大のメリットとなるでしょう。チャネルを作成することは、かなり安価な作業です。それは時間がかかる開始時にすべてを設定しています。

私は個々のチャンネルをキャッシュしません - 私はそれらを作成し、操作のために使用して、それらを閉じます。それらをキャッシュすると、タイムアウトしてチャネルに障害が発生し、それを中止して新しいチャネルを作成する必要があります。

シングルトンを使用してChannelFactoryを実装する理由がわからない場合、特に作成してキャッシュし、エンドポイントが1つしかない場合は特にそうです。

もう少し時間がある場合は、後でサンプルコードを投稿します。

UPDATE:コード例

ここでは、私が仕事でプロジェクトのためにこれを実装する方法の例です。私はChannelFactory<T>を使っていました。私が開発していたアプリケーションは、いくつかのサービスを持つn-tierアプリケーションであり、さらに多くが追加されます。目標は、アプリケーションのライフサイクルごとにクライアントを作成し、必要に応じて通信チャネルを作成する簡単な方法を持つことでした。アイデアの基本は私のものではありません(私はウェブ上の記事から入手しました)が、私のニーズに合わせて実装を変更しました。

私は自分のアプリケーションに静的ヘルパークラスを持っています。そのクラス内には、辞書と、channelfファクトリから通信チャネルを作成するメソッドがあります。

ディクショナリは次のとおりです(オブジェクトは、サービスごとに1つずつ、異なるチャネルファクトリを含むため、値です)。この例では、「キャッシュ」をプレースホルダの一種として配置しています。使用しているキャッシングメカニズムに置き換えてください。

public static Dictionary<string, object> OpenChannels 
{ 
    get 
    { 
     if (Cache["OpenChannels"] == null) 
     { 
      Cache["OpenChannels"] = new Dictionary<string, object>(); 
     } 

     return (Dictionary<string, object>)Cache["OpenChannels"]; 
    } 
    set 
    { 
     Cache["OpenChannels"] = value; 
    } 
} 

次に、ファクトリインスタンスから通信チャネルを作成する方法です。このメソッドは、ファクトリが最初に存在するかどうかをチェックします。ファクトリが存在しない場合は作成し、辞書に入れてからチャネルを生成します。それ以外の場合は、単にファクトリのキャッシュされたインスタンスからチャネルを生成します。

public static T GetFactoryChannel<T>(string address) 
{ 

    string key = typeof(T.Name); 

    if (!OpenChannels.ContainsKey(key)) 
    { 
     ChannelFactory<T> factory = new ChannelFactory<T>(); 
     factory.Endpoint.Address = new EndpointAddress(new System.Uri(address)); 
     factory.Endpoint.Binding = new BasicHttpBinding(); 
     OpenChannels.Add(key, factory); 
    } 

    T channel = ((ChannelFactory<T>)OpenChannels[key]).CreateChannel(); 

    ((IClientChannel)channel).Open(); 

    return channel; 
} 

私は仕事で使っているものからこの例を削除しました。この方法では、複数のバインディングを処理したり、認証用の資格情報を割り当てるなど、クライアントを生成するためのワンストップ・ショッピング・センターを利用できます。

最後に、アプリケーションで使用すると、一般的にチャネルを作成し、ビジネスを行い、必要に応じて終了します(または中止する)。例:

IMyServiceContract client; 

try 
{ 
    client = Helper.GetFactoryChannel<IMyServiceContract>("http://myserviceaddress"); 

    client.DoSomething(); 

    // This is another helper method that will safely close the channel, 
    // handling any exceptions that may occurr trying to close. 
    // Shouldn't be any, but it doesn't hurt. 
    Helper.CloseChannel(client); 
} 
catch (Exception ex) 
{ 
    // Something went wrong; need to abort the channel 
    // I also do logging of some sort here 
    Helper.AbortChannel(client); 
} 

上記の例を参考にしてください。私はこれに類似した何かを生産現場で約1年間使用してきました。とてもうまく機能しています。私たちが遭遇した問題の99%は、通常、アプリケーション外のもの(私たちの直接の管理下にない外部クライアントまたはデータソース)に関連しています。

明確でないことやその他のご質問がある場合は教えてください。

+0

ありがとうTim。あなたは本当に貴重な情報を提供しました。私は間違いなくあなたの例を探しているでしょう! – Didaxis

+0

@ user384080 - コードは私の答えです。それが明確でない場合は、私に知らせてください。ありがとう。 – Tim

+0

@Tim実装にバグがあります。アドレスが何であっても契約タイプによって工場をキャッシュします。契約タイプと住所の両方を含むキーが必要です。 – Anubis

5

あなたは常にだけでは、.NET 3.5からのプロキシオブジェクトはチャネルファクトリによってパフォーマンス上の理由のためにプールされていることに注意してください...各WCFの契約のためのあなたのChannelFactoryが静的

を作ることができます。 ICommunicationObject.Close()メソッドを呼び出すと、実際にオブジェクトをプールに戻して再利用できるようになります。

あなたがコード工場で行う1つのIO呼び出しを防ぐことができれば、チャネルファクトリで行う最適化をはるかに上回る可能性があるので、最適化を行う場合はプロファイラを調べます。最適化する領域を選んではいけません。プロファイラを使用して、最適化の対象となる場所を見つけてください。たとえば、SQLデータベースを使用している場合は、クエリーで果物の価値が低いことがあります。

3

チャネルを作成すると、パフォーマンスが大幅に低下します。実際には、純粋なChannelFactoryではなくクライアントでClientBaseを使用する場合、WCFはChannelFactoryのキャッシュメカニズムを既に持っています。しかし、あなたがいくつかの操作を行うと、キャッシュは期限切れになります。 ErOxの問題については、私はそれが良いと思う別の解決策を得ました。以下を参照してください。

さらに、別の要件がある場合は、自分でInitiateメソッドのロジックとパラメータをカスタマイズできます。このイニシエータクラスは1つのエンドポイントに限定されません。アプリケーションのすべてのエンドポイントにとって強力です。うまくいけば。それはあなたのためにうまくいく。 BTW。この解決策は私のものではありません。私は本からこれを得た。

+0

'lock'が正しく使われていないことに注意してください。ロックは 'ContainsKey'の呼び出しでも取られるべきです。 –

関連する問題