そこで、WCFアプリケーションのパフォーマンスを少し上げて、ChannelsとChannelFactoryをキャッシュすることにしました。これらすべてについて、始める前に解決する必要のある2つの質問があります。
1)ChannelFactoryはシングルトンとして実装する必要がありますか?
2)個々のチャネルをキャッシュ/再利用する方法がわかりません。共有できるこれを行う方法の例はありますか?
私のWCFサービスは、エンドポイントが1つだけのスタンドアロンアプリケーションとして展開されていることに注意することがおそらく重要です。
編集:
ご回答ありがとうございます。私はまだいくつか質問があります...
1)キャッシュをどこで行うべきかについて混乱していると思います。このコードを使用するクライアントAPIを社内の別の部門に配信しています。このキャッシュはクライアントで発生しますか?
2)クライアントAPIはSilverlightアプリケーションの一部として使用されますが、これによって何か変更はありますか?特に、そのようなシナリオで利用できるキャッシュメカニズムは何ですか?
3)GetChannelFactoryメソッドの設計についてはまだはっきりしていません。サービスが1つしかない場合、ChannelFactoryを1つだけ作成してキャッシュする必要がありますか?
私はまだキャッシュ機能を実装していません(それがどのように行われるべきかについて完全に混乱しているためです!)が、これまでのところクライアントプロキシ用に持っているものは次のとおりです。
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();
}
}
}
ChannelFactoryを使用してファクトリのインスタンスを作成し、そのインスタンスをキャッシュします。その後、キャッシュされたインスタンスから必要に応じて/必要に応じてコミュニカティノチャネルを作成できます。
複数のチャネルファクトリが必要ですか(つまり、複数のサービスがありますか)?私の経験では、ここでパフォーマンスに最大のメリットが見られます。チャネルの作成はかなり安価な作業です。開始時にすべてをセットアップするのに時間がかかります。
個々のチャネルをキャッシュすることはありません。チャネルを作成し、操作に使用してから閉じます。それらをキャッシュすると、タイムアウトしてチャネルに障害が発生する可能性があります。その場合は、それを中止して、とにかく新しいチャネルを作成する必要があります。
シングルトンを使用してChannelFactoryを実装する理由がわからない場合、特にそれを作成してキャッシュする場合で、エンドポイントが1つしかない場合はなおさらです。
後でもう少し時間があれば、サンプルコードを投稿します。
更新:コード例
これは、作業中のプロジェクトにこれを実装する方法の例です。私が開発していたアプリケーションはいくつかのサービスを備えたn層アプリであり、さらに追加されるため、ChannelFactory<T>
を使用しました。目標は、アプリケーションの存続期間ごとに1回クライアントを作成し、必要に応じて通信チャネルを作成する簡単な方法を用意することでした。必要に応じて実装を変更しましたが、アイデアの基本は私のものではありません(Web上の記事から入手しました)。
アプリケーションに静的ヘルパークラスがあり、そのクラス内に、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%は、通常、アプリケーションの外部(外部クライアントまたは直接制御されていないデータソース)に関連しています。
不明な点がある場合や、さらに質問がある場合はお知らせください。
WCFコントラクトごとにChannelFactoryを静的にすることができます...
.Net 3.5以降、プロキシオブジェクトは、パフォーマンス上の理由からチャネルファクトリによってプールされることに注意してください。 ICommunicationObject.Close()
メソッドを呼び出すと、オブジェクトが再利用できることを期待して、実際にオブジェクトがプールに返されます。
最適化を行いたい場合はプロファイラーを検討します。コードで1つのIO呼び出しが行われるのを防ぐことができれば、チャネルファクトリで行う最適化をはるかに上回る可能性があります。最適化する領域を選択するのではなく、プロファイラーを使用して、最適化をターゲットにできる場所を見つけてください。たとえば、SQLデータベースがある場合は、クエリにぶら下がっている成果があり、パフォーマンスが大幅に向上します。これらがまだ最適化されていない場合。
チャネルを作成すると、パフォーマンスが大幅に低下します。実際、純粋なChannelFactoryの代わりにクライアントでClientBaseを使用する場合、WCFにはすでにChannelFactoryのキャッシュメカニズムがあります。ただし、いくつかの基本的な操作を行うと、キャッシュは期限切れになります(必要に応じて詳細をグーグルで検索してください)。 ErOxの問題については、別の解決策がありました。下記参照:
namespace ChannelFactoryCacheDemo
{
public static class ChannelFactoryInitiator
{
private static Hashtable channelFactories = new Hashtable();
public static ChannelFactory Initiate(string endpointName)
{
ChannelFactory channelFactory = null;
if (channelFactories.ContainsKey(endpointName))//already cached, get from the table
{
channelFactory = channelFactories[endpointName] as ChannelFactory;
}
else // not cached, create and cache then
{
channelFactory = new ChannelFactory(endpointName);
lock (channelFactories.SyncRoot)
{
channelFactories[endpointName] = channelFactory;
}
}
return channelFactory;
}
}
class AppWhereUseTheChannel
{
static void Main(string[] args)
{
ChannelFactory channelFactory = ChannelFactoryInitiator.Initiate("MyEndpoint");
}
}
interface IMyContract { }
}
別の要件がある場合は、Initiateメソッドのロジックとパラメーターを自分でカスタマイズできます。ただし、このイニシエータークラスは1つのエンドポイントだけに限定されません。これは、アプリケーションのすべてのエンドポイントにとって強力です。うまくいけば。それはあなたのためにうまくいきます。ところで。この解決策は私からのものではありません。私はこれを本から手に入れました。