WCF从理论到实践(15):响应变化

本文目的
需求变化是软件开发过程中的一大难题,我们经常扼腕叹息:面对变化,我们的软件为何如此不堪一击?我们常常在众多需求变化导致的功能爆炸中疲于奔命,甚至迷失自我!这到底为什么?面对"拥抱变化"这种得呐喊,我们的感受应该是震耳欲聋,还是振聋发聩?如果你仍在困惑,可以来看一看WCF是如何摆脱这种困境的!
序幕
小王效力于北京的一家系统集成公司,该公司内部有一个WCF服务为各个部门所共用,小王便是WCF服务的开发和维护人员。起初大家在一个办公楼工作,共用一个局域网,WCF运行的非常好,小王也因此获得了公司上下的一致好评。此时他们的WCF的使用状态如下图所示:


起初小王以为公司都在一个局域网之中,考虑到性能,WCF最早采用的是NetTcpBinding的绑定方式,契约部分是根据自己的业务需要开发的,而他为服务公布的最早的地址为:net.tcp://127.0.0.1:6547/Service。他最早是用代码方式将服务托管到一个Console程序中的,托管代码如下:

ServiceHost host =
new ServiceHost(typeof(ServiceLib.Service));

NetTcpBinding bind
=
new NetTcpBinding();

Uri address
=
new Uri("net.tcp://127.0.0.1:6547/Service");

host.AddServiceEndpoint(
typeof(Contracts.IService), bind, address);

而此时大家所使用的客户端代码均为:


NetTcpBinding bind
=
new NetTcpBinding();

EndpointAddress address
=
new EndpointAddress("net.tcp://127.0.0.1:6547/Service");

Proxys.IService ws
=
new Proxys.ServiceClient(bind, address);


这样一种情况下,小王的WCF服务与各个部门之间可谓是其乐融融。可好景不长。各位看官,请看下文:
使用范围扩大
随着近几年公司业务的持续发展,公司在唐山的业务量非常大,所以在唐山成立了一个办事处,而该办事处同样需要使用小王的WCF服务,可原本出乎小王意料的事情发生了:唐山办事处和公司总部处于不同的网络,如果还使用tcp这种传输方式,就不适用了。公司头们为这事也挺犯愁,本来以为还要劳烦小王大改一通,没想到小王却说:"小意思,马上解决问题!"他首先和公司网络管理人员联系,将WCF服务所在主机配置1个公网IP为202.120.120.3,然后再托管程序的代码中增加几行

BasicHttpBinding bind =
new BasicHttpBinding();

Uri address
=
new Uri("http:// 202.120.120.30/Service");

host.AddServiceEndpoint(
typeof(Contracts.IService), bind, address);

然后将客户端程序场景中的代码修改如下:


BasicHttpBinding bind =
new BasicHttpBinding();

EndpointAddress address
=
new EndpointAddress("http:// 202.120.120.30/Service");

Proxys.IService ws
=
new Proxys.ServiceClient(bind, address);


然后将托管程序重新发布了一下。而将修改之后的客户端部署到办事处一台服务器上面,经过测试,新的客户端运行正常,而总部那些客户端也没受任何影响。由此可见,双方相安无事,小王也松了一口气,原本另大家比较头疼的变化问题便迎刃而解了。下面是使用范围变化之后,WCF服务与客户端的工作情况:




停电了,殃及池鱼
昨天请假了,今天兴致勃勃来上班,没想到刚来,却被经理劈头盖脸的数落了一顿:"昨天停电,来电后,你的服务没有启动,打你电话也不接,下次注意亚!",小王抓抓了头皮,不好意思的笑笑说:"好,我马上想办法,防止出现类似问题",回到座位上,小王就想到了用iis来替代console作宿主,这样机器重新启动后,服务便会自动加载,可转念一想,不对,iis支持http的访问方式,而且基地址必须和服务所在网站地址一致,我们局域网内部采用的是tcp,排除了iis,小王很快就想到了windowsservice,恩,就它了。所以问题也便成了下图所示的情形:


经过查证,小王先弄明白了下面的原理:


不仅如此,在不同宿主中寄宿的方法也一致:都是采用ServiceHost来进行托管。知道上面的知识,小王将原本Console宿主中的托管代码拷贝到Windows服务的代码中,只不过,Console中托管代码写在Main方法,而WindowsService写在OnStart方法中。
秀才遇见兵
原本只是公司内部使用的WCF服务,前段时间也开放给了公司的几个大的合作商,和他们的平台作整合,前几家,合作的非常好,小王正津津乐道之时,却遇到了一个刺头,他是公司一个非常重要的合作商,但要求也非常特殊,服务中原本有个数据契约,用于描述公司订单,契约的定义如下:

本文目的
需求变化是
软件开发过程中的一大难题,我们经常扼腕叹息:面对变化,我们的软件为何如此不堪一击?我们常常在众多需求变化导致的功能爆炸中疲于奔命,甚至迷失自我!
这到底为什么?面对"拥抱变化"这种得呐喊,我们的感受应该是震耳欲聋,还是振聋发聩?如果你仍在困惑,可以来看一看WCF是如何摆脱这种困境的!
序幕
小王效力于北京的一家系统集成公司,该公司内部有一个WCF服务为各个部门所共用,小王便是WCF服务的开发和维护人员。起初大家在一个办公楼工作,共用一个局域网,WCF运行的非常好,小王也因此获得了公司上下的一致好评。此时他们的WCF的使用状态如下图所示:

起初小王以
为公司都在一个局域网之中,考虑到性能,WCF最早采用的是NetTcpBinding的绑定方式,契约部分是根据自己的业务需要开发的,
而他为服务公布的最早的地址为:net.tcp://127.0.0.1:6547/Service。他最早是用代码方式将服务托管到一个Console
程序中的,托管代码如下:
ServiceHost host = new ServiceHost(typeof(ServiceLib.Service));



NetTcpBinding bind = new NetTcpBinding();



Uri address = new Uri("net.tcp://127.0.0.1:6547/Service");



host.AddServiceEndpoint(typeof(Contracts.IService), bind, address);


而此时大家所使用的客户端代码均为:


NetTcpBinding bind = new NetTcpBinding();



EndpointAddress address = new EndpointAddress("net.tcp://127.0.0.1:6547/Service");



Proxys.IService ws = new Proxys.ServiceClient(bind, address);




这样一种情况下,小王的WCF服务与各个部门之间可谓是其乐融融。可好景不长。各位看官,请看下文:
使用范围扩大
随着近几年
公司业务的持续发展,公司在唐山的业务量非常大,所以在唐山成立了一个办事处,而该办事处同样需要使用小王的WCF服务,可原本出乎小王意料的事情发生
了:唐山办事处和公司总部处于不同的网络,如果还使用tcp这种传输方式,就不适用了。公司头们为这事也挺犯愁,本来以为还要劳烦小王大改一通,没想到小
王却说:"小意思,马上解决问题!"他首先和公司网络管理人员联系,将WCF服务所在主机配置1个公网IP为202.120.120.3,然后再托管程序
的代码中增加几行
BasicHttpBinding bind = new BasicHttpBinding();



Uri address = new Uri("http:// 202.120.120.3:80/Service");



host.AddServiceEndpoint(typeof(Contracts.IService), bind, address);


然后将客户端程序场景中的代码修改如下:

BasicHttpBinding bind = new BasicHttpBinding();



EndpointAddress address = new EndpointAddress("http:// 202.120.120.3:80/Service");



Proxys.IService ws = new Proxys.ServiceClient(bind, address);




然后将托管程序重新发布了一下。而将修改之后的客户端部署到办事处一台服务器上面,经过测试,新的客户端运行正常,而总部那些客户端也没受任何影
响。由此可见,双方相安无事,小王也松了一口气,原本另大家比较头疼的变化问题便迎刃而解了。下面是使用范围变化之后,WCF服务与客户端的工作情况:





停电了,殃及池鱼
昨天请假
了,今天兴致勃勃来上班,没想到刚来,却被经理劈头盖脸的数落了一顿:"
昨天停电,来电后,你的服务没有启动,打你电话也不接,下次注意亚!",小王抓抓了头皮,不好意思的笑笑说:"好,我马上想办法,防止出现类似问题",回
到座位上,小王就想到了用iis来替代console作宿主,这样机器重新启动后,服务便会自动加载,可转念一想,不对,iis支持http的访问方式,
而且基地址必须和服务所在网站地址一致,我们局域网内部采用的是tcp,排除了iis,小王很快就想到了windows
service,恩,就它了。所以问题也便成了下图所示的情形:

经过查证,小王先弄明白了下面的原理:

不仅如此,
在不同宿主中寄宿的方法也一致:都是采用ServiceHost来进行托管。知道上面的知识,小王将原本Console宿主中
的托管代码拷贝到Windows服务的代码中,只不过,Console中托管代码写在Main方法,而Windows
Service写在OnStart方法中。
秀才遇见兵
原本只是公
司内部使用的WCF服务,前段时间也开放给了公司的几个大的合作商,和他们的平台作整合,前几家,合作的非常好,小王正津津乐道之时,却遇到了一个刺头,
他是公司一个非常重要的合作商,但要求也非常特殊,服务中原本有个数据契约,用于描述公司订单,契约的定义如下:

旧版本数据协定

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
[url]http://www.CodeHighlighter.com/[/url]

-->[DataContract]



public class Order



{



private string _orderID;



[DataMember]



public string OrderID



{



get



{



return _orderID;



}



set



{



_orderID = value;



}



}



private string _content;



[DataMember]



public string Content



{



get



{



return _content;



}



set



{



_content = value;



}



}



}






而且其他的合作商,对此都没有任何的异议,小王将代理已经发出去10几家了,可现在遇到这位要求必须再上面的基础上再提供订单的创建时间,小王打电话过去,和他解释:"目前我们的系统已经被很多厂商使用,而且别人的代理目前也运行的很好,你的这个需求是正确的,但我们如果修改数据契约会影响其它客户端的使用,请理解",本以为自己苦口婆心的解释,人家会买账,谁想,对方根本不听解释,一个劲地说:"这是我们的需求,必须的,必须的!"。气得小王直想摔电话。可"用户是上帝,胳膊扭不过大腿么",后来一高人指点道:"WCF在设计的时候已经考虑到了版本管理的问题,这种问题已经非常容易解决,你可以用IExtensibleDataObject",经查阅还真有此类问题的解决方案,小王将上面的契约代码更改如下:

[DataContract]



public class Order:IExtensibleDataObject



{



private string _orderID;



[DataMember]



public string OrderID



{



get



{



return _orderID;



}



set



{



_orderID = value;



}



}



private string _content;



[DataMember]



public string Content



{



get



{



return _content;



}



set



{



_content = value;



}



}







private ExtensionDataObject _exData;



public ExtensionDataObject ExtensionData



{



get



{



return _exData;



}



set



{



_exData = value;



}



}







private DateTime _createTime;



public DateTime CreateTime



{



get



{



return _createTime;



}



set



{



_createTime = value;



}



}



}


然后新生成一个代理,发给了刁蛮用户,而其他客户的代理版本却不用任何变动。新的用户的代理运行和很正常,而老的也很正常,真爽!小王此时方体会到WCF的强大之处。


凡是符合下面的变动情形的,均可视为非重大更改:
  • 更改成员名称,且将DataMemberAttribute的Name属性和旧版本保持一致
  • 大多数添加和移除数据成员的操作
  • 将成员的特性IsRequired从True更改为False
  • 实现IExtensibleDataObject接口
  • 更改枚举成员名称,但用EnumMemberAttribute将协定名称和老版本保持一致
  • 大多数数据集合的更改
服务也会累么?
问题是一个接一个,解决完上面的几个问题没多长时间,这个WCF又有新问题了,现在客户端越来越多,很多客户反映,最近服务端很不稳定,有时还连接不上去,小王检察了半天业务代码,发现没有问题,而且之前人数少得时候,运行的挺好的亚,此时他经过仔细的查阅,终于找到了一些用的信息:WCF为了改善性能,是有限流(Throttling)措施的。主要包括三种
  • maxConcurrentCalls :最大并发数,默认为16
  • maxConcurrentSessions :最大的会话数,主要针对于PerSession的情况,默认为10
  • maxConcurrentInstances:最大实例数,默认为Int.MaxValue
默认的设置比较保守,最大并发数仅为16,非常容易造成排队或者超时现象。修改或者添加限流也非常简单,只需要在服务的配置中修改或者增加ServiceBehavior中的

<serviceThrottling maxConcurrentCalls="16" maxConcurrentSessions="10"
/>



后记
拥抱变化,拥抱变化,我们口号喊得非常响亮,可为什么面对变化的时候,我们有时会暴跳如雷呢?一边骂客户bt,一边还要通宵达旦的加班来完成工作呢?问题的原因不在于用户,不在于变化本身,更多的是我们一味的将一堆垃圾改造成另外一堆垃圾,要想真正解决问题,做好架构,做好基础是关键。我们常常因为时间,人力等因素,刹不住车似的的制造garbage,却忘记了什么叫细节,什么叫品质。无论针对产品还是市场,当前更重视不是你做了多少,而是做了多少有意义的事情,多少别人没有做过或者做不了的事情。品质,细节,ThinkPad,Google。总之我会毫不犹豫地转向WCF,因为它有品,也更能适应变化



原文出处:http://www.cnblogs.com/jillzhang/