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.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服务,前段时间也开放给了公司的几个大的合作商,和他们的平台作整合,前几家,合作的非常好,小王正津津乐道之时,却遇到了一个刺头,他是公司一个非常重要的合作商,但要求也非常特殊,服务中原本有个数据契约,用于描述公司订单,契约的定义如下:

旧版本数据协定

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

新版本数据协定

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

凡是符合下面的变动情形的,均可视为非重大更改:

  1. 更改成员名称,且将DataMemberAttribute的Name属性和旧版本保持一致
  2. 大多数添加和移除数据成员的操作
  3. 将成员的特性IsRequired从True更改为False
  4. 实现IExtensibleDataObject接口
  5. 更改枚举成员名称,但用EnumMemberAttribute将协定名称和老版本保持一致
  6. 大多数数据集合的更改

服务也会累么?

问题是一个接一个,解决完上面的几个问题没多长时间,这个WCF又有新问题了,现在客户端越来越多,很多客户反映,最近服务端很不稳定,有时还连接不上去,小王检察了半天业务代码,发现没有问题,而且之前人数少得时候,运行的挺好的亚,此时他经过仔细的查阅,终于找到了一些用的信息:WCF为了改善性能,是有限流(Throttling)措施的。主要包括三种

  1. maxConcurrentCalls :最大并发数,默认为16
  2. maxConcurrentSessions :最大的会话数,主要针对于PerSession的情况,默认为10
  3. maxConcurrentInstances:最大实例数,默认为Int.MaxValue

默认的设置比较保守,最大并发数仅为16,非常容易造成排队或者超时现象。修改或者添加限流也非常简单,只需要在服务的配置中修改或者增加ServiceBehavior中的

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

 

后记

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

作者:jillzhang
出处:http://jillzhang.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
Tag标签: WCF
posted @ 2008-04-16 17:59 Robin Zhang 阅读(3611) 评论(26)  编辑 收藏 网摘 所属分类: WCF

  回复  引用  查看    
#1楼[楼主]2008-04-16 18:06 | jillzhang      
诚邀各位对WCF感兴趣的兄弟们加入
小组名称:WCF技术研究
地址:http://space.cnblogs.com/group/100319/" target="_new">http://space.cnblogs.com/group/100319/

  回复  引用    
#2楼2008-04-16 18:43 | 苏州婚纱[未注册用户]
楼主的WCF方面的文章我都收藏了.谢谢哦。 
  回复  引用  查看    
#3楼2008-04-16 19:44 | good man      
请问楼主有没有最基础的关于WCF的东西 啊
我想从最基础学习起走,我看你的文章有一点晕啊
写得太好啊,学过,路过,看过
支持,告诉我一下基础的学习东西 哟

  回复  引用  查看    
#4楼2008-04-16 20:00 | 生鱼片      
学习
  回复  引用  查看    
#5楼[楼主]2008-04-16 20:04 | jillzhang      
@苏州婚纱
@生鱼片
多谢支持
@good man
你可以看<<WCF服务编程>>这本书

  回复  引用  查看    
#6楼2008-04-16 20:53 | 平静中的疯狂      
http://www.cnblogs.com/Emoticons/yoyocici/cool.gif" alt="" />刚刚买到这本书,WCF,WFC,SL掀起新一轮学习高潮http://www.cnblogs.com/Emoticons/face/020.gif" alt="" />
  回复  引用  查看    
#7楼2008-04-16 22:11 | 篮外空心      
问博主:
我刚刚写个例子
OperationContract返回的是List<string>
但是客户端调用返回的是string[]
被改造了?

  回复  引用    
#8楼2008-04-16 22:13 | WCF[未注册用户]
对WCF感兴趣的请加QQ群:13960774
  回复  引用  查看    
#9楼2008-04-16 22:20 | 篮外空心      
再问个。。。
vs2008里新建wcf项目。。那个是托管在什么下的。。

  回复  引用  查看    
#10楼[楼主]2008-04-16 22:23 | jillzhang      
@篮外空心
这个道理很简单
1)List<string>不是一种通用类型,在.net里面有,可在java,php等其他语言可能就很难找到对应类型,而WCF可以跨平台,所以不应该返回list<string>,而应该返回通用类型Array
2)为何要用List<string>?,如果在编译前,就知道集合中有多少个元素,那相比就没必要用List<string>了吧?而你想想,WCF序列化的是对象,不是类,而且这个类是已经确定的了,所以序列化的时候,集合数量是已知的了,所以也没有必要返回List<string>了亚

  回复  引用  查看    
#11楼[楼主]2008-04-16 22:23 | jillzhang      
@篮外空心
自托管

  回复  引用  查看    
#12楼2008-04-16 22:31 | 篮外空心      
@jillzhang
还处在不知所以然阶段,谢谢博主指明。。。

我改用string[]去。。

再谢。。

  回复  引用    
#13楼2008-04-16 23:07 | erway[未注册用户]
楼主,这篇文章写得太好了,不仅有内容,而且语言也很轻松活泼,现在很需要这样的技术类书籍。
  回复  引用  查看    
#14楼2008-04-17 08:24 | 1-2-3      
非常好!!
  回复  引用  查看    
#15楼[楼主]2008-04-17 09:42 | jillzhang      
@erway
@1-2-3
多谢你们的支持

  回复  引用  查看    
#16楼2008-04-17 13:36 | 李战      
http://www.cnblogs.com/Emoticons/qface/055243188.gif" alt="" />强大啊!
  回复  引用  查看    
#17楼2008-04-18 03:33 | 镜涛      
呵呵,学习一下。也想加入到团队中来,一起学习交流。呵呵
  回复  引用  查看    
#18楼2008-06-18 16:52 | flank.chen      
BasicHttpBinding bind = new BasicHttpBinding();

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

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

有个问题:当使用范围扩大后,你的意思是增加上面这段服务吗?也就是说服务里有两种方式,客户端可随意调用一种是吗?

  回复  引用  查看    
#19楼[楼主]2008-06-18 17:24 | jillzhang      
@flank.chen
上面一段代码是增加服务终结点的
一个服务可以包含多个终结点,但一个服务不能有相同形式的终结点,比如不能包含两个http的终结点,http,tcp,net.pipe这样可以。
客户端可以根据具体情况任意选择调用哪一个

  回复  引用  查看    
#20楼2008-06-19 09:12 | flank.chen      
哦,那就是说我的理解没错哦。谢谢。
  回复  引用  查看    
#21楼2008-06-19 09:14 | flank.chen      
最近要做一个WCF和SilverLight结合的流媒体项目,正在学习中,时间很紧,有没有好的类似项目可供学习?
  回复  引用  查看    
#22楼2008-12-08 10:22 | leonardleonard      
写的非常好



发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1156649




相关文章:

相关链接: