究竟应该怎么调用WCF服务?
对于下面几种方式,哪一种是最合适的,我期望的是这样的:
1) 在客户端,对于TCP确保使用连接池,在每一次使用连接之后归还连接等待复用。由于连接池是宝贵的资源,不可能每一次调用都去创建,而是只创建一次。
2) 在服务端,调用完毕之后可以尽快释放服务实例(设置的是 [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)])
3) 对于多线程环境,是线程安全的并且也是高性能的,可以利用多线程环境提高速度的。
我想了一下有这些写法,每一种测试分别用TCP和HTTP信道运行一遍:
我分别测试了:
1) 用ClientBase的方式(也就是添加服务引用后生成的ServiceClient的方式)
2) 用ChannelFactory方式(直接复用服务契约的方式)
3) 是否主动开关的区别(主动开关是否会影响性能?是否必要?是否会影响连接的复用?)
4) 是否共享的区别(共享后是否有性能提高?是否线程安全?)
Parallel.For(0, it, (i, loopState) =>
{
using (ServiceClient client = new ServiceClient(s))
{
try
{
if (client.Add(1, 2) != 3) throw new Exception("error");
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
loopState.Break();
}
}
});
Console.WriteLine(s + " 每次创建 Client:" + sw.ElapsedMilliseconds + "," + it * 1000 / sw.ElapsedMilliseconds);
2)
Parallel.For(0, it, (i, loopState) =>
{
using (ServiceClient client = new ServiceClient(s))
{
client.Open();
try
{
if (client.Add(1, 2) != 3) throw new Exception("error");
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
loopState.Break();
}
client.Close();
}
});
Console.WriteLine(s + " 每次创建 Client (主动开关):" + sw.ElapsedMilliseconds + "," + it * 1000 / sw.ElapsedMilliseconds);
3)
using (ServiceClient client = new ServiceClient(s))
{
client.Open();
Parallel.For(0, it, (i, loopState) =>
{
try
{
if (client.Add(1, 2) != 3) throw new Exception("error");
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
loopState.Break();
}
});
client.Close();
}
Console.WriteLine(s + " 共享 Client:" + sw.ElapsedMilliseconds + "," + it * 1000 / sw.ElapsedMilliseconds);
4)
using (ServiceClient client = new ServiceClient(s))
{
client.Open();
Parallel.For(0, it, (i, loopState) =>
{
try
{
if (client.Add(1, 2) != 3) throw new Exception("error");
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
loopState.Break();
}
});
client.Close();
}
Console.WriteLine(s + " 共享 Client (主动开关):" + sw.ElapsedMilliseconds + "," + it * 1000 / sw.ElapsedMilliseconds);
5)
Parallel.For(0, it, (i, loopState) =>
{
using (ChannelFactory<IGeneralCalculator> cf = new ChannelFactory<IGeneralCalculator>(s))
{
try
{
var client = cf.CreateChannel();
if (client.Add(1, 2) != 3) throw new Exception("error");
(client as ICommunicationObject).Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
loopState.Break();
}
}
});
Console.WriteLine(s + " 每次创建 ChannelFactory:" + sw.ElapsedMilliseconds + "," + it * 1000 / sw.ElapsedMilliseconds);
6)
Parallel.For(0, it, (i, loopState) =>
{
using (ChannelFactory<IGeneralCalculator> cf = new ChannelFactory<IGeneralCalculator>(s))
{
cf.Open();
try
{
var client = cf.CreateChannel();
if (client.Add(1, 2) != 3) throw new Exception("error");
(client as ICommunicationObject).Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
loopState.Break();
}
cf.Close();
}
});
Console.WriteLine(s + " 每次创建 ChannelFactory (主动开关):" + sw.ElapsedMilliseconds + "," + it * 1000 / sw.ElapsedMilliseconds);
7)
using (ChannelFactory<IGeneralCalculator> cf = new ChannelFactory<IGeneralCalculator>(s))
{
Parallel.For(0, it, (i, loopState) =>
{
try
{
var client = cf.CreateChannel();
if (client.Add(1, 2) != 3) throw new Exception("error");
(client as ICommunicationObject).Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
loopState.Break();
}
});
}
Console.WriteLine(s + " 共享 ChannelFactory:" + sw.ElapsedMilliseconds + "," + it * 1000 / sw.ElapsedMilliseconds);
8)
using (ChannelFactory<IGeneralCalculator> cf = new ChannelFactory<IGeneralCalculator>(s))
{
cf.Open();
Parallel.For(0, it, (i, loopState) =>
{
try
{
var client = cf.CreateChannel();
if (client.Add(1, 2) != 3) throw new Exception("error");
(client as ICommunicationObject).Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
loopState.Break();
}
});
cf.Close();
}
Console.WriteLine(s + " 共享 ChannelFactory (主动开关):" + sw.ElapsedMilliseconds + "," + it * 1000 / sw.ElapsedMilliseconds);
测试结果如下(Y坐标是每秒操作执行成功数):
1) HTTP总体性能比TCP差不少,但是不管哪种方式都差不多,我猜测差不多的原因是因为HTTP是短连接而TCP是长连接的关系。
2) 按我的想法,每次创建ChannelFactory性能是最差的,可能是每一次都在使用新的连接池,事实也是这样,但是也没有差太多
3) 我本来以为共享ChannelFactory和每次创建Client的方式性能差不多的(因为ClientBase会缓存ChannelFactory),但是差距还是挺大的
4) 共享Client方式能有这么高的性能让人意外,我本来以为会和每次创建Client一样,不知这样用会不会有什么问题
官方的文档中虽然是每次创建Client或ChannelFactory后及时关闭的,但他那个都是控制台程序其实怎么做都不重要,对于ASP.NET多线程环境中的使用究竟哪种最靠谱?
我个人觉得两种方式比较靠谱的(没看到官方有相关的最佳实践的说法,自己猜的):
1) 如果使用ClientBase(ServiceClient)方式的,每一次使用都创建,使用后及时关闭
2) 如果使用ChannelFactory方式的,共享方式使用,但是使用后及时关闭Create出来的Channel
反正不管怎么样,测试结论还是和自己想的不太一样,请大家讨论。
作者: lovecindywang 发表于 2011-03-17 14:33 原文链接