十五天精通WCF——第一天 三种Binding让你KO80%的业务
转眼wcf技术已经出现不少年了,也在.net界混的风生水起,同时.net也是一个高度封装的框架,做为在wcf食物链最顶端的咱们所能作的任务已经简单的不能再简单了,html
再简单的话马路上的大妈也能写wcf了,好了,wcf最基本的概念咱们放在后面慢慢分析,下面咱们来看看神奇的3个binding如何KO咱们实际场景中的80%的业务场景。程序员
一:basicHttpBindingweb
做为入门第一篇,也就不深刻谈谈basic中的信道栈中那些啥东西了,你只须要知道有ABC三个要素,注意不是姨妈巾哦,若是须要详细了解,能够观赏我之前的系列。在编程
这里我就很少说了,太简单的东西没意思,先看个例子简单感觉了,你只需知道的是basic走的是http协议就行了,传输消息为soap。json
1. 契约windows
1 using System.Runtime.Serialization; 2 using System.ServiceModel; 3 4 namespace MyService 5 { 6 [ServiceContract] 7 public interface IHomeService 8 { 9 [OperationContract] 10 int GetLength(string name); 11 } 12 }
2. 实现类api
1 using System; 2 using System.Messaging; 3 using System.Threading; 4 5 namespace MyService 6 { 7 public class HomeService : IHomeService 8 { 9 public int GetLength(string name) 10 { 11 return name.Length; 12 } 13 } 14 }
3. 服务启动浏览器
1 using System; 2 using System.ServiceModel; 3 4 namespace MyService 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 using (ServiceHost host = new ServiceHost(typeof(HomeService))) 11 { 12 try 13 { 14 host.Open(); 15 16 Console.WriteLine("服务开启!"); 17 18 Console.Read(); 19 } 20 catch (Exception e) 21 { 22 Console.WriteLine(e.Message); 23 } 24 } 25 } 26 } 27 }
4. 配置config文件安全
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <bindings> <netTcpBinding> <binding name="IHomeServiceBinding" /> </netTcpBinding> </bindings> <behaviors> <serviceBehaviors> <behavior name=""> <serviceMetadata httpGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="true" /> </behavior> </serviceBehaviors> </behaviors> <services> <service name="MyService.HomeService"> <endpoint address="http://127.0.0.1:1920/HomeService" binding="basicHttpBinding" contract="MyService.IHomeService"> <identity> <dns value="localhost" /> </identity> </endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> <host> <baseAddresses> <add baseAddress="http://127.0.0.1:1920"/> </baseAddresses> </host> </service> </services> </system.serviceModel> </configuration>
5. 而后经过 servicehost 启动服务端服务器
using System; using System.ServiceModel; namespace MyService { class Program { static void Main(string[] args) { using (ServiceHost host = new ServiceHost(typeof(HomeService))) { try { host.Open(); Console.WriteLine("服务开启!"); Console.Read(); } catch (Exception e) { Console.WriteLine(e.Message); } } } } }
好了,到如今为止,服务端所有开启完毕,接下来咱们经过“添加服务引用”,来添加对客户端的引用
1 using System; 2 3 namespace ConsoleApplication1 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 HomeServiceReference.HomeServiceClient client = new HomeServiceReference.HomeServiceClient(); 10 11 var s = client.GetLength("12345"); 12 13 Console.WriteLine("长度为:{0}", s); 14 15 Console.Read(); 16 } 17 } 18 }
麻蛋,就这么简单,是的,就这样简单的五步,基于http的通讯就这样被不当心的完成了,真很差意思。
二:netTcpBinding
有了basic的代码,如今咱们要改为tcp通讯,这会通讯走的是字节流,很简单,改一下服务端的config文件就行了,你们也知道这种性能要比basic好。
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <behaviors> <serviceBehaviors> <behavior name="mxbehavior"> <serviceMetadata httpGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="true" /> </behavior> </serviceBehaviors> </behaviors> <services> <service name="MyService.HomeService" behaviorConfiguration="mxbehavior"> <endpoint address="net.tcp://localhost:19200/HomeService" binding="netTcpBinding" contract="MyService.IHomeService"> <identity> <dns value="localhost" /> </identity> </endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> <host> <baseAddresses> <add baseAddress="http://localhost:1920/HomeService"/> </baseAddresses> </host> </service> </services> </system.serviceModel> </configuration>
三:netMsmqBinding
msmq这个玩意,我想你们都清楚,一个物理上的文件,好处呢,你也明白,就是client和service的全部通讯都要通过它的手,这样任何一方出了问题,只要
它在就没问题了。一样咱们把tcp改为msmq也是很是简单的,不过要注意,msmqbinding中是不可让契约方法有返回值的。因此咱们加上isoneway就行了。
using System.Runtime.Serialization; using System.ServiceModel; namespace MyService { [ServiceContract] public interface IHomeService { [OperationContract(IsOneWay = true)] void GetLength(string name); } }
而后我在mmc上新建一个消息队列,以下:
而后咱们再改动如下配置文件
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <behaviors> <serviceBehaviors> <behavior name="mxbehavior"> <serviceMetadata httpGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="true" /> </behavior> </serviceBehaviors> </behaviors> <bindings> <netMsmqBinding> <binding name="msmqbinding"> <security mode="None"/> </binding> </netMsmqBinding> </bindings> <services> <service name="MyService.HomeService" behaviorConfiguration="mxbehavior"> <endpoint address="net.msmq://localhost/private/homequeue" binding="netMsmqBinding" contract="MyService.IHomeService" bindingConfiguration="msmqbinding"> <identity> <dns value="localhost" /> </identity> </endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> <host> <baseAddresses> <add baseAddress="http://localhost:19200/HomeService"/> </baseAddresses> </host> </service> </services> </system.serviceModel> </configuration>
纵观上面的三种binding,配置起来何其简单,底层的各类通信协议貌似对我来讲都是透明的,其实呢???wcf在底层作了何其多的事情,而我却没有挖掘。。。
这对码农里说也是一种悲哀啊。。。出了问题就只能祷告上天。。。下一篇我会开始深刻剖析。
十五天精通WCF——次日 告别烦恼的config配置
常常搞wcf的基友们确定会知道,当你的应用程序有不少的“服务引用”的时候,是否是有一种疯狂的感受。。。从一个环境迁移到另一个环境,你须要改变的
endpoint会超级tmd的多,简直就是搞死了人。。。好了,这篇咱们来看看如何最小化配置。
一:精简service的config配置
就像上一篇的代码同样,个人service端的config配置以下:
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <system.servicemodel> 4 <behaviors> 5 <servicebehaviors> 6 <behavior name="mxbehavior"> 7 <servicemetadata httpgetenabled="true" /> 8 <servicedebug includeexceptiondetailinfaults="true" /> 9 </behavior> 10 </servicebehaviors> 11 </behaviors> 12 <services> 13 <service name="myservice.homeservice" behaviorconfiguration="mxbehavior"> 14 <endpoint address="net.tcp://localhost:1920/homeservice" binding="nettcpbinding" contract="myservice.ihomeservice"> 15 <identity> 16 <dns value="localhost" /> 17 </identity> 18 </endpoint> 19 <endpoint address="mex" binding="mexhttpbinding" contract="imetadataexchange" /> 20 <host> 21 <baseaddresses> 22 <add baseaddress="http://localhost:19200/homeservice"/> 23 </baseaddresses> 24 </host> 25 </service> 26 </services> 27 </system.servicemodel> 28 </configuration>
经过上面的代码,你应该知道在system.servicemodel下的全部节点都是wcf专属的节点,全部的节点数据都会被开启servicehost这个监听器时捕获到,下面我能够
经过servicehost这个监听器的源码下面找找相关的读取config节点的代码。
经过上面的截图,你是否是有一种感受,就是service的底层也是经过代码动态的读取config下面的节点来获取数据,那就意味着我能够直接将代码写入到code中,
对吧,这样我就能够把我认为该配置的东西配置起来,不应配置的东西所有放到代码里面去,这样个人灵活性是否是很是的强大。。。。爽吧,说干就干。。。
1 class Program1 2 { 3 static void Main(string[] args) 4 { 5 ServiceHost host = new ServiceHost(typeof(HomeService), new Uri("http://localhost:19200/HomeService")); 6 7 host.AddServiceEndpoint(typeof(IHomeService), new NetTcpBinding(), "net.tcp://localhost:1920/HomeService"); 8 9 //公布元数据 10 host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true }); 11 host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex"); 12 13 host.Open(); 14 15 Console.WriteLine("服务已经开启。。。"); 16 17 Console.Read(); 18 } 19 }
有人就要说了,地址的话确定不能是写死的,必须变活,简单啊,我就仅仅把ip地址配置到config里面去不就完事了,对不对。
<configuration> <appSettings> <add key ="baseurl" value="http://localhost:19200/HomeService"/> <add key ="endpoindurl" value="net.tcp://localhost:1920/HomeService"/> </appSettings>
1 class Program1 2 { 3 static void Main(string[] args) 4 { 5 ServiceHost host = new ServiceHost(typeof(HomeService), new Uri(ConfigurationManager.AppSettings["baseurl"])); 6 7 host.AddServiceEndpoint(typeof(IHomeService), new NetTcpBinding(), ConfigurationManager.AppSettings["endpoindurl"]); 8 9 //公布元数据 10 host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true }); 11 host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex"); 12 13 host.Open(); 14 15 Console.WriteLine("服务已经开启。。。"); 16 17 Console.Read(); 18 } 19 }
如今看的话,是否是清楚多了,若是你以为个人代码比较累赘,你能够封装成一个方法,而后就能够动态的配置nettcp,basic,ws*等等对吧。。。好了,说完服
务端,接下来咱们看看client端如何避免。
二:精简client的config配置
就像上一节那样,若是我用“服务引用”的话,vs会偷偷的用svcutil.exe来给咱们生成一个proxy类和一个config文件,proxy类也就是你看到的xxxclient。。。
可恶的是config里面会给我生成一些乱七八糟的东西,以下图:
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <system.serviceModel> 4 <bindings> 5 <netTcpBinding> 6 <binding name="NetTcpBinding_IHomeService" /> 7 </netTcpBinding> 8 </bindings> 9 <client> 10 <endpoint address="net.tcp://localhost:1920/HomeService" binding="netTcpBinding" 11 bindingConfiguration="NetTcpBinding_IHomeService" contract="HomeServiceReference.IHomeService" 12 name="NetTcpBinding_IHomeService"> 13 <identity> 14 <dns value="localhost" /> 15 </identity> 16 </endpoint> 17 </client> 18 </system.serviceModel> 19 </configuration>
同服务器端同样,若是我用code作掉,是否是很是的爽呢???那可不能够作掉呢? 咱们还得看一下proxy的源码,首先你会看到其实所谓的proxy只是一个继承
自clientbase的一个类,以下图。
上面的两幅图,你会发现,最后的proxy类是经过ChannelFactory<TChannel>类来完成助攻的,那话说回来了,既然底层用了ChannelFactory<TChannel>,
那何不我在代码里面就用ChannelFactory<TChannel>不是更好吗???这样config也省了,对吧,说干就干啦。。。
1 static void Main(string[] args) 2 { 3 ChannelFactory<IHomeService> factory = new ChannelFactory<IHomeService>(new NetTcpBinding(), "net.tcp://localhost:1920/homeservice"); 4 5 var channel = factory.CreateChannel(); 6 7 var result = channel.GetLength("12345"); 8 }
好了,代码就这么简单,如今是否是感受本身萌萌大啦~~~
十五天精通WCF——第三天 client如何知道server提供的功能清单
一般咱们去大保健的时候,都会找姑娘问一下这里能提供什么服务,什么价格,这时候可能姑娘会跟你口述一些服务或者提供一份服务清单,这样的话大
家就能够作到童嫂无欺,这样一份活生生的例子,在wcf中一样是一个道理,只有client了解service能提供哪些功能,client才能够根据server提供的功能进行
消费,那问题来了,service怎么把功能提供给client进行选择呢???这个就是我这一篇要聊的wsdl(web service description language)。。。
一:wsdl
如今你已经知道了,wsdl就是server提供给client的清单,那下面问题就来了。server是如何提供的呢???你要是比较仔细的话,可能会知道我在上一
篇提到的一个endpoint,以下截图。
在上面这幅图中,你能够看到,Homeservice提供了两个端点,一个是“服务端点“,一个是“元数据端点”。而且你也看到了,元数据的端点地址是
http://192.168.16.16:19200/mex,当client经过svcutil访问这个地址的时候,就拿到了server能提供的功能清单,而后client就能够根据这些功能生成一
个代理文件,而后的而后,就是你懂得,各类啪啪啪,XXXClient。
二:眼见为实
1.见证wsdl
要想看见wsdl,你只须要经过http://localhost:19200打开服务地址、以下图:
而后点击:http://localhost:19200/?singleWsdl
如今你看到的就是server功能清单,太tmd的重量级了,已经完彻底全果体在世人前了,下一小节咱们再详细的分析下。
2. 见证client端的XXXclient
刚才我也说了,当你用vs作“服务引用”的时候,svcutil会根据http://localhost:19200/mex的地址来查看wsdl,而后生成代理,下面咱们具体来看一下。
点击肯定以后,咱们就能够看到在 Service References 文件夹下面生成了一个Reference.cs 文件。
而后咱们打开Reference.cs,就能够看到一个继承于ClientBase的HomeServiceClient。
三:详细分析wsdl文件
学wcf,你必定要像svcutil同样可以看得懂wsdl。
1. 首先看下server提供了一个Update操做,参数是一个id,一个Student这个自定义的复杂类型,同时返回也是Student这个
复杂类型。
1 namespace MyService 2 { 3 [ServiceContract] 4 public interface IHomeService 5 { 6 [OperationContract] 7 Student Update(int id, Student stu); 8 } 9 }
2. wsdl这个xml文件,刚才你也看到了,下面咱们一个个节点看看
<1> portType 和 operation节点
当你看到下面的截图后,我想你也能猜的出来,portType就是契约(IHomeService),operation就是契约方法(Update),不过有点意思的是,在operation
下面你看到了一个input,一个output,这个就是所谓的 ”输入消息“,”输出消息”,那是什么意思呢??? 也就是说client到server的消息叫作“输入消息”,server到
client端叫作“输出消息”,到这里你应该彷佛明白了,我C#中的Update方法是有入参和出参的,然而这映射到wsdl中就是两条消息,input和output,这个也就是经典
的“请求-响应“模式。
好了,继续往下看,在wsdl:input和wsdl:output中分别有一个Action属性,这个很是有意思,wcf的底层就是经过这个地址来找到对应的方法,好比咱们看到的代理
类中的Update方法上面就有这么一段。
<2> message 和 types节点
继续往下看的话,你会发现input和output中还有一个message属性,对应的为IHomeService_Update_InputMessage和IHomeService_Update_OutputMessage,
这个正好是message节点的引用,以下图:
从这个图中,你能够看到input和output下面都有一个wsdl:part节点,这个就是代表input和output中须要携带的参数,好比element="tns:Update",就引用了
element中Name=Update的节点,以下图:
好了,最后我再截一张图,能够看到,传输协议为soap,服务地址等等。。。而后就没什么好说的了。
十五天精通WCF——第四天 你必定要明白的通讯单元Message
转眼你已经学了三天的wcf了,是否是很好奇wcf在传输层上面到底传递的是个什么鸟毛东西呢???应该有人知道是soap,那soap这叼毛长得是什么
样呢?这一篇咱们来揭开答案。。。
一:soap到底长成什么样子
为了能看清soap长的啥样,我能够用强大的Fiddler来监视一下,忽然好激动啊!!!
1.Server
1 static void Main(string[] args) 2 { 3 ServiceHost host = new ServiceHost(typeof(HomeService), new Uri("http://192.168.1.105:19200")); 4 5 host.AddServiceEndpoint(typeof(IHomeService), new BasicHttpBinding(), "HomeServie"); 6 7 //公布元数据 8 host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true }); 9 10 host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex"); 11 12 host.Open(); 13 14 Console.WriteLine("服务已经开启。。。"); 15 16 Console.Read(); 17 }
2.Client
1 ChannelFactory<IHomeService> factory = new ChannelFactory<IHomeService>(new BasicHttpBinding(), new EndpointAddress("http://192.168.1.105:19200/HomeServie")); 2 3 var client = factory.CreateChannel(); 4 5 client.Update("王八蛋");
如今我想你大概看清楚了这玩意是个么样子,一个创建在xml上面的一种消息格式,根元素是envelope,我知道这叼毛翻译过来就是“信封”,因此就有了”封头“
和”封体”,就是s:Header 和 s:Body,从这个soap中你能够看到它忽略了header,而后咱们继续往下看,还记得Update的意思吗???若是你读懂了上一篇,
你应该知道这是一个Action,也就是所谓的input消息。与之对应的就是UpdateResponse这个output消息,对吧,还记得xmlns="http://tempuri.org/">吗?
它就是IHomeService的默认命名空间,对吧。。。
下一个咱们关注的是Update这个Action中的<str>这个,你也看获得,这个就是上图中Update方法中的str参数,最后咱们来看一下UpdateResponse中
的<UpdateResult xmlns:a="http://schemas.datacontract.org/2004/07/MyService,不知道你是否还记得它就是WSDL中关于Student的XSD结
构,看下图:
好了,wcf中的soap结构咱们也大概了解了一下,不知道有没有引起你对soap更深刻的思考呢???
二:对soap的更深刻思考
经过fiddler观察,你应该也明白了,无论是客户端仍是服务端,wcf的高层封装都是仅仅拿出了Envelope中的body节点,而其余节点对咱们来讲好像并
没有什么卵用,好比我说的Header节点,这么说来,Header是否是有点浪费呢???那下面有一个问题来了,wcf在底层用什么来构造消息的呢???下面
咱们大概找下client端的源码。。。
经过上面的图,你如今应该也知道了在.net中其实tmd的就是message构造的,因此我想告诉你的是:既然wcf在底层也是用message来构造的,何不我本身
就来构造message消息呢???岂不美哉???这样我就能够随意操做message,对吧。。。否则wcf这个高层封装的叼毛,对我来讲就是一种束缚。。。因
为我已经知道了service公布的wsdl,因此我能够轻松构造message。。。
三:用message来调用Server端
废话很少说,构造message你必定要知道下图中的三点:(input这个Action,契约方式 和 服务地址)。
好了,下面我先来构造数据契约,指定服务契约的命名空间 和 Action在Soap中的名称
1 [DataContract(Namespace = "http://tempuri.org/", Name = "Update")] 2 class Test 3 { 4 [DataMember] 5 public string str { get; set; } 6 }
而后,我把这个数据契约塞到envelope中的body中,以下:
1 BasicHttpBinding bingding = new BasicHttpBinding(); 2 3 BindingParameterCollection param = new BindingParameterCollection(); 4 5 var u = new Test() { str = "王八蛋" }; 6 7 Message request = Message.CreateMessage(MessageVersion.Soap11, "http://tempuri.org/IHomeService/Update", u); 8 9 IChannelFactory<IRequestChannel> factory = bingding.BuildChannelFactory<IRequestChannel>(param); 10 11 factory.Open(); 12 13 IRequestChannel channel = factory.CreateChannel(new EndpointAddress("http://192.168.1.105:19200/HomeServie")); 14 15 channel.Open(); 16 17 var result = channel.Request(request); 18 19 channel.Close(); 20 21 factory.Close();
接下来,咱们跑起来看一下,效果咋样。。。
看没看到,这个就是我手工构造的Message,是否是太帅了。。。哈哈,太帅的应该在后面,刚才也说了,既然你们玩的都是Message,而你这个几把wcf却仅仅把
个人message.body拿出来了,那干脆我直接在契约方法中加message岂不是更好么???自由操做Message还有个什么好处呢??固然啦,我能够在Message的
Header中加一些参数token,client的ip地址,client的身份,client的时间等等这些统计信息,对吧。。。这样才是最帅的,好了,说干就干,咱们修改下server端的
契约方法,只用来接受Message。
server端:
1 public class HomeService : IHomeService 2 { 3 public Message Update(Message message) 4 { 5 var header = message.Headers; 6 7 var ip = header.GetHeader<string>("ip", string.Empty); 8 9 var currentTime = header.GetHeader<string>("currenttime", string.Empty); 10 11 //这个就是牛逼的 统计信息。。。 12 Console.WriteLine("客户端的IP=" + ip + " 当前时间=" + currentTime); 13 14 return Message.CreateMessage(message.Version, message.Headers.Action + "Response", "等我吃完肯德基,再打死你这个傻逼!!!"); 15 } 16 }
client端:
1 namespace ConsoleApplication1 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 BasicHttpBinding bingding = new BasicHttpBinding(); 8 9 BindingParameterCollection param = new BindingParameterCollection(); 10 11 var u = new Test() { str = "王八蛋" }; 12 13 Message request = Message.CreateMessage(MessageVersion.Soap11, "http://tempuri.org/IHomeService/Update", u); 14 15 //在header中追加ip信息 16 request.Headers.Add(MessageHeader.CreateHeader("ip", string.Empty, Dns.GetHostByName(Dns.GetHostName()).AddressList[0].ToString())); 17 request.Headers.Add(MessageHeader.CreateHeader("currenttime", string.Empty, DateTime.Now)); 18 19 IChannelFactory<IRequestChannel> factory = bingding.BuildChannelFactory<IRequestChannel>(param); 20 21 factory.Open(); 22 23 IRequestChannel channel = factory.CreateChannel(new EndpointAddress("http://192.168.1.105:19200/HomeServie")); 24 25 channel.Open(); 26 27 var result = channel.Request(request); 28 29 channel.Close(); 30 31 factory.Close(); 32 } 33 } 34 35 [DataContract(Namespace = "http://tempuri.org/", Name = "Update")] 36 class Test 37 { 38 [DataMember] 39 public string str { get; set; } 40 } 41 }
而后咱们用Fiddler监视一下结果:
如今一切都如我所愿,好了,我想你也大概明白了这个神奇的message,也不要忘了它就是wcf的基本通讯单元,我要去吃肯德基了。。。。。。
十五天精通WCF——第五天 你须要了解的三个小技巧
一: 服务是端点的集合
当你在开发wcf的时候,你或许已经注意到了一个service能够公布多个endpoint,确实是这样,在wcf中有一句很经典的话,叫作“服务是端点的集合",就
好比说一个普普统统的服务,它就公布了一个服务端点,一个元数据端点,对吧。。。
仔细一想,这个问题就好玩了,既然一个service能够公布多个endpoint,并且我还知道wcf中有不少的binding,这些binding对应着不少的传输方式,那是否是
说我一个service能够用多种协议方法对外公布,好比说同时以nettcp,basic,msmqbinding,udp等方式公布,对吧,那这样的话是否是超级好玩,若是对方
是非.net程序,那就能够调用个人basic,若是对方是.net程序,那是否是能够调用个人nettcp,对不对。。。固然啦,wcf无所不能,这是一个史上无比强大的牛
逼框架,牛逼的要死,已经逼得程序员只需随便改几个配置就能达到彻底不同的效果。。。下面我同时用nettcp和basic的方式来同时公布服务,好了,如今我
们就来见证奇迹吧。。。
Service:
1 using System; 2 using System.Runtime.Serialization; 3 using System.ServiceModel; 4 using System.ServiceModel.Channels; 5 using System.Threading; 6 7 namespace MyService 8 { 9 public class HomeService : IHomeService 10 { 11 public Student Update(Student message) 12 { 13 return new Student() { Name = "一线码农" }; 14 } 15 } 16 17 [DataContract] 18 public class Student 19 { 20 [DataMember] 21 public string Name { get; set; } 22 23 [DataMember] 24 public int Age { get; set; } 25 } 26 }
Host :
1 class Program1 2 { 3 static void Main(string[] args) 4 { 5 ServiceHost host = new ServiceHost(typeof(HomeService), new Uri("http://192.168.1.105:1920")); 6 7 host.AddServiceEndpoint(typeof(IHomeService), new BasicHttpBinding(), "HomeServie"); 8 9 host.AddServiceEndpoint(typeof(IHomeService), new NetTcpBinding(), "net.tcp://192.168.1.105:1921/HomeServieTcp"); 10 11 host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true }); 12 13 host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex"); 14 15 host.Open(); 16 17 Console.Read(); 18 } 19 }
Client端:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 //basic 方式 6 ChannelFactory<IHomeService> factory = new ChannelFactory<IHomeService>(new BasicHttpBinding(), 7 new EndpointAddress("http://192.168.1.105:1920/HomeServie")); 8 9 var client = factory.CreateChannel(); 10 11 var result = client.Update(new Student() { }); 12 13 14 //nettcp方式 15 factory = new ChannelFactory<IHomeService>(new NetTcpBinding(), 16 new EndpointAddress("net.tcp://192.168.1.105:1921/HomeServieTcp")); 17 18 client = factory.CreateChannel(); 19 20 result = client.Update(new Student() { }); 21 } 22 }
经过上面的代码,是否是已经发现,我在client端,既能够用basic的方式调用,又能够用nettcp的方式调用,这个技巧是否是感受wcf无比强大呢???
二:Host寄宿多个Service
咱们知道wcf的寄宿方式有不少种,有iis,有windowservice,还有简单方便的console方式,而默认状况下,咱们最一般的方法都是一个service,一个寄宿,
而其实呢??? 其实一个寄宿host能够承载多个service,看起来是否是很好玩,若是说你有10个servcie,如今你只须要用一个console host就能寄宿起来,废
话很少说,我演示一下给你看就行了。
Service:
1 namespace MyService 2 { 3 [ServiceContract] 4 public interface IHomeService 5 { 6 [OperationContract] 7 Student Update(Student message); 8 } 9 10 [ServiceContract] 11 public interface IFlyService 12 { 13 [OperationContract] 14 Student Fly(Student stu); 15 } 16 }
Host:
1 class Program1 2 { 3 static void Main(string[] args) 4 { 5 //第一个: 这是Home服务 6 ServiceHost host = new ServiceHost(typeof(HomeService), new Uri("http://192.168.1.105:1920")); 7 host.AddServiceEndpoint(typeof(IHomeService), new BasicHttpBinding(), "HomeServie"); 8 host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true }); 9 host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex"); 10 host.Open(); 11 12 Console.WriteLine("Home服务开启。。。。"); 13 14 //第一个: 这是Fly服务 15 var host2 = new ServiceHost(typeof(FlyService), new Uri("http://192.168.1.105:1930")); 16 host2.AddServiceEndpoint(typeof(IFlyService), new BasicHttpBinding(), "FlyServie"); 17 host2.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true }); 18 host2.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex"); 19 host2.Open(); 20 21 Console.WriteLine("Fly服务开启。。。。"); 22 23 Console.Read(); 24 } 25 }
有没有看到,如今两个服务都开启了,这种方式看起来是否是很爽呀,不然的话,你须要开启两个Host,这样的话,个人手续就精简了。。。对吧。。
三: Tcp中的端口共享
这玩意听起来你们都懂,端口共享嘛,不就是两个程序共享一个端口,对吧,在一般状况下,咱们确定会认为这没法作到,其实呢?在Wcf中咱们仍是能够玩
的,也就是一个PortSharingEnabled的事!!!若是说端口能够共享的话,那咱们的service是否是就能够少开辟几个端口呢?一样这也方便咱们进行service的管
理,下面我给你们继续演示一下。。。很好玩的,么么哒
能够看到,个人两个host都是用1920的端口,而且如今我真的开启起来啦。。。。好了,三种技巧都说到了,我想你在现实的wcf开发中,或多或少的都能接
触的到,但愿对你有用~~~~
十五天精通WCF——第六天 你必需要了解的3种通讯模式
wcf已经说到第六天了,竟然尚未说到这玩意有几种通讯模式,惭愧惭愧,不过很简单啦,单向,请求-响应,双工模式,其中的第二种“请求-响应“
模式,这个你们不用动脑子都清楚,这一篇我大概来分析下。
一:“请求-响应“模式
若是你看了我上一篇的博文,你应该很是清楚这种相似“本地调用”的方式,wcf一样也分为“同步”和“异步”两种,不过无论是异步仍是同步,最终都逃
不过是“请求-响应”这个事实,对吧。
1: 同步方式
这种方式我想没什么好说的,前面几篇我已经说的很是清楚了,具体使用方法能够参考个人前面几篇文章。。。谢啦~~~~
2: 异步方式
一般咱们都有这样的一个思惟,遇到耗时的东西第一反应就想到了多线程,毕竟多线程也是一种负载均衡,在wcf这种”请求-响应“模式,一样也支持异
步,很神奇吧,并且神奇到能够在“服务引用“界面上作到一键生成,什么???你不信!!!!不信你看。。。
而后我很是好奇的看下XXXClient给咱们生成的是个什么代码。。。
经过client端的proxy代码,你能够清楚的看到,这鸡巴WCF真的不容易,给咱们生成了两种“异步模式”,第一种是最古老的beginXXX,endXXX模式,
还有一种是被Jeffrey Richter 严重鄙视的“事件异步模式”。。。没什么好说的,截图一下给你们看看。
二:“单向“模式
不少时候,咱们或许都有这样的需求,好比说订单提交成功的时候,我须要给客户发送邮件,可是你想一想,我发送邮件这个任务只是我订单流程的
一个“额外任务“,也就是说,它的失败不该该会阻止个人订单流程,而且它的逻辑时间不该该会阻碍个人下单总时间,对吧。。。这样的话,个人订单时
间才会最小化,为了达到不影响下单总时间的效果,个人想法就是,client端直接把消息丢给信道就行了,而后无论server端有没有真的接收到,处理的
慢不慢,过的好很差,等等,很是开心的是,这些对wcf来讲真的是小菜一碟,只须要一个轻轻松松的”IsOneWay=true“属性就能够了。。。牛逼的要
死。。。还有就是由于是单向的,因此契约方法就没有存在返回值的必要了,我说的对吧。。。嘿嘿~~~
1 [ServiceContract] 2 public interface IHomeService 3 { 4 [OperationContract(IsOneWay = true)] 5 void Update(Student message); 6 }
1 namespace MyService 2 { 3 public class HomeService : IHomeService 4 { 5 public void Update(Student message) 6 { 7 Console.WriteLine(message.Name); 8 } 9 } 10 11 [DataContract] 12 public class Student 13 { 14 [DataMember] 15 public string Name { get; set; } 16 17 [DataMember] 18 public int Age { get; set; } 19 } 20 }
为了验证是否真的是单向通信,我能够用二种方法验证下。
1. wsdl中是否有output这个message
经过下面的图,我想你看的很清楚了,你再也没有找到咱们熟悉的“output”这个message,这就说明貌似真的是单向的了,由于wsdl就是web服务的清单。
2. 使用fillder监视一下请求消息
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 HomeServiceClient client = new HomeServiceClient(); 6 7 client.Update(new Student() { Name = "hxc" }); 8 } 9 }
正如我在图中说的那样,很是奇怪,个人IsOneWay模式,居然在http模式下行不通,可是你要记住,http模式天生就是“请求-响应”模式,它彻底作不了
单向模式,说明白一点就是:“wcf发现你的bingding不支持单向“的时候,它并不会报错,仍是用本身天生的”请求-相应“模式来模拟”单向通讯“,这就是你
看到的很是奇怪的Http 202这个http状态码,不少人包括我,都不知道http202 是几个意思,不要紧,咱们百科一下就行了。。。下面框框的里面的字,
已经说的很是清楚了,感谢感谢。。。
三:“双向“ 模式
这个通信其实没什么好讲的,也只有tcp模式才会天生支持,而http模式天生就不支持,就像上面同样,若是非要用http来支持“双向通信“,那又是在
坑"wcf"他爹,这样就会逼着他爹在底层再创建一个“请求-响应“模式来支持所谓的”双向通信“,并且”双向通信“这个玩意还不如用两个单向的”请求-响应”模
式或者两个“单向模式”来支持,并且两个”请求-响应“模式比”双向通信“有更大的灵活性,反正我是对它不感冒,了解一下便可,若是你们比较感兴趣,能够
在wcf官网上看一下:https://msdn.microsoft.com/zh-cn/library/ms735119.aspx。
好了,就说到这里,洗洗睡了,晚安~~~~
十五天精通WCF——第七天 Close和Abort到底该怎么用才对得起观众
一:文起原因
写这一篇的目的源自于最近看同事在写wcf的时候,用特别感受繁琐并且云里雾里的嵌套try catch来防止client抛出异常,特别感受奇怪,就好比下面的代码。
1 public void StartNormalMarketing(int shopId, List<int> marketingIdList) 2 { 3 4 using (SendEventMarketingService.DistributeServiceClient client = new SendEventMarketingService.DistributeServiceClient()) 5 { 6 try 7 { 8 9 client.StartByMarketingIDList(shopId, marketingIdList, SendEventMarketingService.MarketingType.NormalMarketing); 10 11 } 12 catch (Exception ex) 13 { 14 LogHelper.WriteLog("常规营销活动开启服务", ex); 15 } 16 finally 17 { 18 try 19 { 20 client.Close(); 21 } 22 catch (Exception) 23 { 24 client.Abort(); 25 } 26 } 27 } 28 }
看完上面的代码,不知道你是否有什么感想?并且我还问了同事,为何try catch要写成这样,同事说是根据什么书上来的什么最佳实践,这话一说,我也不敢轻易
怀疑了,只能翻翻源代码看看这话是否有道理,首先我来讲说对这段代码的第一感受。。。
1. 代码特别繁琐
咱们写代码,特别不喜欢繁琐,上面的代码就是一例,你try catch就try catch,还在finally中嵌套一个try catch,真的有点感受像吃了两只癞蛤蟆同样。。。
2. 混淆close和abort的用法
这种代码给人的感受就是为何不精简一下呢???好比下面这样,起码还能够少写一对try catch,对吧。
1 public void StartNormalMarketing(int shopId, List<int> marketingIdList) 2 { 3 4 using (SendEventMarketingService.DistributeServiceClient client = new SendEventMarketingService.DistributeServiceClient()) 5 { 6 try 7 { 8 9 client.StartByMarketingIDList(shopId, marketingIdList, SendEventMarketingService.MarketingType.NormalMarketing); 10 11 client.Close(); 12 } 13 catch (Exception ex) 14 { 15 LogHelper.WriteLog("常规营销活动开启服务", ex); 16 17 client.Abort(); 18 } 19 } 20 }
并且乍一看这段代码和文中开头那一段代码貌似实现同样,可是某些人的“最佳实践”却不是这样,因此确实会致使我这样的后来人犯迷糊,对吧。。。反正我就是头晕,
简直就是弄糊涂到何时该用close,何时该用abort。。。
二:探索原理
为了弄明白到底可不能够用一个try catch来替代之,下面咱们一块儿研究一下。
1. 从代码注释角度甄别
从类库的注释中,能够比较有意思的看出,abort方法仅仅比close多一个“当即”,再无其余,有意思,不过这对我来讲并无什么卵用,由于这个注释太
笼统了,为了让本身更加完全的明白,只能来翻看下close和abort的源代码。
2. 从源码角度甄别
为了方便让ILSpy调试Client代码,如今我决定用ChannelFactory来代替,以下图:
1 namespace ConsoleApplication1 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 ChannelFactory<IHomeService> factory = new ChannelFactory<IHomeService>(); 8 9 try 10 { 11 var channel = factory.CreateChannel(); 12 13 factory.Close(); 14 } 15 catch (Exception ex) 16 { 17 factory.Abort(); 18 } 19 } 20 } 21 }
为了让你们更好的理解,我把close方法的源码提供以下:
1 // System.ServiceModel.Channels.CommunicationObject 2 [__DynamicallyInvokable] 3 public void Close(TimeSpan timeout) 4 { 5 if (timeout < TimeSpan.Zero) 6 { 7 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("timeout", SR.GetString("SFxTimeoutOutOfRange0"))); 8 } 9 using ((DiagnosticUtility.ShouldUseActivity && this.TraceOpenAndClose) ? this.CreateCloseActivity() : null) 10 { 11 CommunicationState communicationState; 12 lock (this.ThisLock) 13 { 14 communicationState = this.state; 15 if (communicationState != CommunicationState.Closed) 16 { 17 this.state = CommunicationState.Closing; 18 } 19 this.closeCalled = true; 20 } 21 switch (communicationState) 22 { 23 case CommunicationState.Created: 24 case CommunicationState.Opening: 25 case CommunicationState.Faulted: 26 this.Abort(); 27 if (communicationState == CommunicationState.Faulted) 28 { 29 throw TraceUtility.ThrowHelperError(this.CreateFaultedException(), Guid.Empty, this); 30 } 31 goto IL_174; 32 case CommunicationState.Opened: 33 { 34 bool flag2 = true; 35 try 36 { 37 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); 38 this.OnClosing(); 39 if (!this.onClosingCalled) 40 { 41 throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnClosing"), Guid.Empty, this); 42 } 43 this.OnClose(timeoutHelper.RemainingTime()); 44 this.OnClosed(); 45 if (!this.onClosedCalled) 46 { 47 throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnClosed"), Guid.Empty, this); 48 } 49 flag2 = false; 50 goto IL_174; 51 } 52 finally 53 { 54 if (flag2) 55 { 56 if (DiagnosticUtility.ShouldTraceWarning) 57 { 58 TraceUtility.TraceEvent(TraceEventType.Warning, 524292, SR.GetString("TraceCodeCommunicationObjectCloseFailed", new object[] 59 { 60 this.GetCommunicationObjectType().ToString() 61 }), this); 62 } 63 this.Abort(); 64 } 65 } 66 break; 67 } 68 case CommunicationState.Closing: 69 case CommunicationState.Closed: 70 goto IL_174; 71 } 72 throw Fx.AssertAndThrow("CommunicationObject.BeginClose: Unknown CommunicationState"); 73 IL_174:; 74 } 75 }
而后我提供一下Abort代码:
1 // System.ServiceModel.Channels.CommunicationObject 2 [__DynamicallyInvokable] 3 public void Abort() 4 { 5 lock (this.ThisLock) 6 { 7 if (this.aborted || this.state == CommunicationState.Closed) 8 { 9 return; 10 } 11 this.aborted = true; 12 this.state = CommunicationState.Closing; 13 } 14 if (DiagnosticUtility.ShouldTraceInformation) 15 { 16 TraceUtility.TraceEvent(TraceEventType.Information, 524290, SR.GetString("TraceCodeCommunicationObjectAborted", new object[] 17 { 18 TraceUtility.CreateSourceString(this) 19 }), this); 20 } 21 bool flag2 = true; 22 try 23 { 24 this.OnClosing(); 25 if (!this.onClosingCalled) 26 { 27 throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnClosing"), Guid.Empty, this); 28 } 29 this.OnAbort(); 30 this.OnClosed(); 31 if (!this.onClosedCalled) 32 { 33 throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnClosed"), Guid.Empty, this); 34 } 35 flag2 = false; 36 } 37 finally 38 { 39 if (flag2 && DiagnosticUtility.ShouldTraceWarning) 40 { 41 TraceUtility.TraceEvent(TraceEventType.Warning, 524291, SR.GetString("TraceCodeCommunicationObjectAbortFailed", new object[] 42 { 43 this.GetCommunicationObjectType().ToString() 44 }), this); 45 } 46 } 47 }
仔细观察完这两个方法,你会发现什么呢???至少我能够提出下面四个问题:
1:Abort是Close的子集吗?
是的,由于若是你看懂了Close,你会发现Close只针对Faulted 和Opened作了判断,而其中在Faulted的枚举下会调用原生的Abort方法。。。以下图
2:我能监视Client的各类状态吗?好比Created,Opening,Fault,Closed等等。。。
固然能够了,wcf的信道老祖宗就是ICommunicationObject,而它就有5种监听事件,这些就能够随时监听,懂伐???
1 static void Main(string[] args) 2 { 3 ChannelFactory<IHomeService> factory = new ChannelFactory<IHomeService>(new BasicHttpBinding(), new EndpointAddress("http://localhost:1920/HomeServie")); 4 5 try 6 { 7 factory.Opened += (o, e) => 8 { 9 Console.WriteLine("Opened"); 10 }; 11 12 factory.Closing += (o, e) => 13 { 14 Console.WriteLine("Closing"); 15 }; 16 17 factory.Closed += (o, e) => 18 { 19 Console.WriteLine("Closed"); 20 }; 21 22 var channel = factory.CreateChannel(); 23 24 var result = channel.Update(new Student() { }); 25 26 factory.Close(); 27 } 28 catch (Exception ex) 29 { 30 factory.Abort(); 31 } 32 }
3:Abort会抛出异常吗?
从这个截图中能够看到很是有意思的一段,那就是竟然abort活生生的把异常给吞了。。。骨头都不给吐出来。。。真tmd的神奇到家了,想一想也有道理,由于只有
这样,咱们上层的代码在catch中才不会二次抛出“未处理异常”了,对吧,再转念看一下Close方法。
从上面图中能够看到,Close在遇到Faulted以后调用Abort方法,若是说Abort方法调用失败,Close方法会再次判断状态,若是仍是Faulted的话,就会向上抛出
异常。。。这就是为何Abort不会抛异常,Close会的缘由,因此Close千万不要放在Catch块中。
4. Abort代码大概都干了些什么
这个问题问的好,要能完美解决的话,咱们看下代码,以下图,从图中能够看到,Abort的大目的就是用来关闭信道,具体会通过closeing,abort和closed这
三个方法,同时,这三个事件也会被老祖宗ICommunicationObject监听的到。
好了,最后咱们关注的一个问题在于下面这条语句是否应该放在Try块中???
1 ChannelFactory<IHomeService> factory = new ChannelFactory<IHomeService>(new BasicHttpBinding(), new EndpointAddress("http://localhost:1920/HomeServie"));
很简单,咱们简要的看一下代码,看里面是否会有“异常”抛出便可。。。。
能够看到,在new的过程当中可能,或许会有异常的产生,因此最好把try catch改为下面这样。。。
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 ChannelFactory<IHomeService> factory = null; 6 try 7 { 8 factory = new ChannelFactory<IHomeService>(new BasicHttpBinding(), new EndpointAddress("http://localhost:1920/HomeServie")); 9 10 var channel = factory.CreateChannel(); 11 12 var result = channel.Update(new Student() { }); 13 14 factory.Close(); 15 16 throw new Exception(); 17 } 18 catch (Exception ex) 19 { 20 if (factory != null) 21 factory.Abort(); 22 } 23 } 24 }
好了,综合我上面所说的一切,我我的以为最好的方式应该是上面这样,夜深了,睡觉了,晚安。
十五天精通WCF——第八天 对“绑定”的最后一点理解
转眼已经中断10几天没有写博客了,也不是工做太忙,正好碰到了端午节,而后最近看天津台的爱情保卫战入迷了。。。太好看了,一直都是耐人寻味。。。并且
涂磊老师话说的真是tmd的经典,而后就这样耽搁了,好了,话很少说,这篇咱们看看binding中最后一点须要知道的东西。
一:信道栈
我在以前的文章中屡次提到信道栈,不知道你们对它的概念是否有了解,其实想一想也仍是蛮简单的,既然是栈,那么这个栈确定就不止一个元素了,对吧,第二个
的话,既然是栈,那么确定就遵循FILO的原则,可能你会说,这个仍是蛮抽象的,能给个具体的例子么???恭喜你,wcf中还真有一个方法CreateBindingElements,
下面咱们具体看看。。。
1. 简单看看各类binding的栈中都有些什么
看到上面的监控窗口,是否是有点意思,在BasicHttpBinding的信道栈中有两个元素,分别是HttpTransportBindingElement和TextMessageEncodingBindingEl
ement,经过名字也能很容易的判断出来,一个是“http传输协议”,一个是“文本消息编码协议”,而后再看看复杂一点的WSHttpBinding,你会发现,他不光有Basic
的全部东西,还包括SymmetricSecurityBindingElement(安全协议) 和 TransactionFlowBindingElement(事务流),如今你心中是否是有底了,起码我知道各
种Binding里面都有些啥,为了更好的理解,我来画一张简图。
上面这个图,大概也就表达了个人意思,当咱们Client在走WSHttpBinding这个协议的时候,Client端的InputMessage会先走 TransactionFlow,SymmetricSec
urity,TextMessageEncoding,最后走HttpTransport,而后Service端就按照客户端进行“反向处理”,经过一阵禁脔以后,咱们就拿到了安全的OutputMessage。
二:BindingElement的跨绑定性
你要是很仔细的话,你确定会发现,其实Binding就是一个预先默认配置好的信道栈,对不对,你也看到了,每一种Binding都有属于本身的BindingElements,
偏偏这些Elements是能够跨Binding的,也就是说我能够自由组合Elements,这样是否是能够给咱们这些寒酸的码农最大的灵活性,对吧,举个简单的例子,
BasicHttpBinding有两个绑定元素,其中对soap消息进行的是TextMessageEncoding编码对吧,而netTcpBinding对soap进行的BinaryMessageEncoding,
而后你也应该知道了,我想作一个自定义的Binding,其中消息编码是BinaryMessage,传输协议是HttpTransport,那怎么作呢????
Host文件:
1 class Program1 2 { 3 static void Main(string[] args) 4 { 5 ServiceHost host = new ServiceHost(typeof(HomeService), new Uri("http://192.168.1.105:1920")); 6 7 var customBinding = new CustomBinding(); 8 9 customBinding.Elements.Add(new BinaryMessageEncodingBindingElement()); 10 customBinding.Elements.Add(new HttpTransportBindingElement()); 11 12 host.AddServiceEndpoint(typeof(IHomeService), customBinding, "HomeServie"); 13 14 host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true }); 15 16 host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex"); 17 18 host.Open(); 19 20 Console.WriteLine("服务已经开启!!!"); 21 22 Console.Read(); 23 } 24 }
Client调用:
1 static void Main(string[] args) 2 { 3 ServiceReference1.HomeServiceClient client = new ServiceReference1.HomeServiceClient(); 4 5 var result = client.Update("你好"); 6 7 Console.WriteLine("server value:" + result); 8 9 Console.Read(); 10 }
最后咱们用Fiddler监视一下,最后咱们看看,都是些乱码。
这篇就说到这里了,但愿对你有帮助,下一篇咱们看看WCF中的Behavior,很好玩的哦~~~
十五天精通WCF——第九天 高级玩法之自定义Behavior
终于我又看完了二期爱情保卫战,太酸爽了,推荐连接:http://www.iqiyi.com/a_19rrgublqh.html?vfm=2008_aldbd,很少说,谁看谁入迷,下面言归正传,
看看这个颇有意思的Behavior。
一: Behavior这个泼妇的厉害
在前面的文章中,我也清楚的说明了整个wcf通讯流,而Behavior这个泼妇能够在wcf通讯流中的任何地方插上一脚,蛮狠无比,利用的好,让你上天堂,利用的不
好,让你下地狱。。。下面让你看看behavior到底有哪些能够注入的点???先画个简图:
上面的图,大概就是wcf的通讯简图,全部蓝色字体都是Behavior注入的点,其中Client和Service端均可以注入,若是按照功能分的话,又能够分为“操做级别”和
”端点级别“,下面我来简要的分解下。
二:端点级别Behavior
从图中你也能够看到,消息检查器是放在Channel这个级别的,也就是说它能够监视Client和Server的入站请求,也就是说全部的请求都须要经过它转发,若是
这样的话,那我是否是能够在这个注入点上自由的修改,变动,拦截入站和出站请求,并且利用这个特性我还能够作不少的事情,好比日志记录,记录统计等等,下
面咱们来看看这个怎么使用??? 只须要extends IEndpointBehavior 和 IDispatchMessageInspector,而后加入EndpointBehaviors便可。。。
1. IDispatchMessageInspector
1 public class MyDispatchMessageInspector : IDispatchMessageInspector 2 { 3 public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) 4 { 5 Console.WriteLine(request.ToString()); 6 return request; 7 } 8 9 public void BeforeSendReply(ref Message reply, object correlationState) 10 { 11 Console.WriteLine(reply.ToString()); 12 } 13 }
2. IEndpointBehavior
1 public class MyEndpointBehavior : IEndpointBehavior 2 { 3 public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) 4 { 5 } 6 7 public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime) 8 { 9 } 10 11 public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher) 12 { 13 endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MyDispatchMessageInspector()); 14 } 15 16 public void Validate(ServiceEndpoint endpoint) 17 { 18 } 19 }
3. 将MyEndpointBehavior加入到Host中
1 static void Main(string[] args) 2 { 3 ServiceHost host = new ServiceHost(typeof(HomeService), new Uri("http://127.0.0.1:1920")); 4 5 host.AddServiceEndpoint(typeof(IHomeService), new BasicHttpBinding(), "HomeServie"); 6 7 host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true }); 8 9 host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex"); 10 11 host.Description.Endpoints[0].EndpointBehaviors.Add(new MyEndpointBehavior()); 12 13 host.Open(); 14 15 Console.WriteLine("服务已经开启!!!"); 16 17 Console.Read(); 18 }
4. 最后咱们看一下服务方法
1 public class HomeService : IHomeService 2 { 3 public string Update(string message) 4 { 5 Console.WriteLine("我在Action方法:" + message); 6 7 return "my reply!!!"; 8 } 9 }
下面看看效果。。。在效果图中,你应该看到了。在个人Action中的方法先后各有一段“入站消息”和“出站消息”,是否是很爽???
三:操做级别Behavior
从文章开头的简图中,你应该看到了,Operation级别的Behavior比较多,有“操做启动器(IOperationInvoker)","参数检查(IParameterInspector)“,
“消息格式化器(IDispatchMessageFormatter)”等等。。。 为何说等等这个词,很简单啊,,,其实还有不少系统内置的,既然是Operation,那就必
然是针对方法的,还记得OperationContract是怎么套在方法上的吗??? 是特性,对吧,,,一样的道理,OperationBehavior也是同样,那怎么用呢??
一样也是很简单的,继承几个接口便可。。。
<1> IParameterInspector 的玩法
其实没什么好说的,既然是属于Operation下面的Behavior,那都是经过特性注入的,而这个IParameterInspector,能够作到相似Mvc的Model验证,下面
我作个简单的Action参数长度验证(长度不超过8个字符)。
1. IParameterInspector
1 public class MyIParameterInspector : IParameterInspector 2 { 3 public int MaxLength { get; set; } 4 5 public MyIParameterInspector(int MaxLength) 6 { 7 this.MaxLength = MaxLength; 8 } 9 10 /// <summary> 11 /// 出站的操做 12 /// </summary> 13 /// <param name="operationName"></param> 14 /// <param name="outputs"></param> 15 /// <param name="returnValue"></param> 16 /// <param name="correlationState"></param> 17 public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState) 18 { 19 20 } 21 22 /// <summary> 23 /// 入站的参数 24 /// </summary> 25 /// <param name="operationName"></param> 26 /// <param name="inputs"></param> 27 /// <returns></returns> 28 public object BeforeCall(string operationName, object[] inputs) 29 { 30 foreach (var item in inputs) 31 { 32 if (Convert.ToString(item).Length > MaxLength) 33 { 34 throw new Exception("码单,长度不能超过 " + MaxLength + " 个长度"); 35 } 36 } 37 38 return null; 39 } 40 }
2. IOperationBehavior
1 public class MyOperationBehavior : Attribute, IOperationBehavior 2 { 3 public int MaxLength { get; set; } 4 5 public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) 6 { 7 8 } 9 10 public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) 11 { 12 13 } 14 15 public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation) 16 { 17 dispatchOperation.ParameterInspectors.Add(new MyIParameterInspector(MaxLength)); 18 } 19 20 public void Validate(OperationDescription operationDescription) 21 { 22 23 } 24 }
3. 在Action在加上MyOperationBehavior 这个 Attribute
1 public class HomeService : IHomeService 2 { 3 [MyOperationBehavior(MaxLength = 5)] 4 public string Update(string message) 5 { 6 Console.WriteLine("我在Action方法:" + message); 7 8 return "my reply!!!"; 9 } 10 }
4. 而后我在客户端故意输入大于5的字符,看看效果怎么样???
1 public class Program1 2 { 3 static void Main(string[] args) 4 { 5 HomeServiceClient client = new HomeServiceClient(); 6 7 client.Update("我故意输入了不少的字符,哈哈。。。。。"); 8 9 Console.Read(); 10 } 11 }
5. 最后看看效果图,能够看到,最终的入站消息会抛出一个异常。。。
<2> MessageFormatter,IOperationInvoker 的玩法
剩下的这两个玩法都差很少,你只须要extends一下,而后加入到OperationBehavior便可,有了上面的思想,我想下面这些使用起来都不是问题吧。。。
十五天精通WCF——第十天 学会用SvcConfigEditor来简化配置
咱们在玩wcf项目的时候,都是本身手工编写system.serviceModel下面的配置,虽然在webconfig中作wcf的服务配置的时候,vs提供大多
数的代码提示,但对于不太熟悉服务配置的小鸟们来讲,有些困难,并且一些服务配置也容易遗漏,大多状况下,咱们都是copy一份服务配置,然
后在服务配置上面修修改改,对吧。。。其实呢,.net给咱们提供了一个强大的scvconfigeditor这个工具化的软件来帮助咱们生成wcf的配置,是
不是很神奇???
一:工具在何处
固然在无比牛逼的Microsoft SDK下面啦,在C:\Program Files (x86)\Microsoft SDKs\Windows下面,你会找到不少的版本,以下图:
对吧,你已经看到了不少的版本,固然啦,我确定要找最新的啦,一禁脔,我进去了v8.0A,以下图:
C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools
你应该也看到了,各类牛逼的工具,很眼馋吧,不过这一篇咱们仍是看重SvcConfigEditor。
二: 如何使用SvcConfigEditor
1. 双击打开,选择“文件” => “新建配置”。
2. 而后咱们选择 “新建服务” => “填写服务名”
3. 而后咱们给service定义一个host, 点击 "主机" => "新建“ => "填写基址"。
4. 到这一步,你是否是特别想看一看生成的config配置是咋样的???好啊,知足你的虚荣心,咱们只须要点
击"保存“,选择一个路径便可。。。
5. 好了,你的虚荣心获得知足了,下面咱们来定义endpoint了,其实也是很是很是简单的, 点击”终结点"
=> "新建服务终结点",而后咱们就象征性的填写一些Address,Contract,Binding便可,以下图:
6. 上面咱们就已经定义了一个basichttpbinding了,下一步的话,咱们还记得要公布一个mexhttpbinding,
这样个人svcutil才能服务引用,对吧,因此方法也是很简单,继续“新建终结点”,以下图:
7. 最后我还记得mex须要有一个behavior,让http的get能够访问,有了这个神器,一样简单,咱们能够
点击“高级” => "服务行为" => "新建"。
8. 最后咱们保存来看一下生成的appconfig是啥样的???
则么样???我不须要写一个字的config配置就完成了基本的服务配置,若是你还想玩高级的,能够本身试着琢磨琢磨SvcConfigEditor。
好了,差很少能够睡了,下一篇咱们来研究研究 SvcConfigEditor中的诊断工具,很好玩的啦~~~~~
十五天精通WCF——第十一天 如何对wcf进行全程监控
说点题外话,咱们在玩asp.net的时候,都知道有一个叼毛玩意叫作“生命周期”,咱们能够用httpmodule在先于页面的page_load中
作一些拦截,这样作的好处有不少,好比记录日志,参数过滤,全局登陆验证等等。。。在wcf里面的话也是有相似的功能,第一种就是在
endpoint中加上runtime的behavior,这样的话就能够先于“服务方法”作拦截,第二种方法呢,也就是咱们这一篇所说的全程监控,俗称
”诊断功能”。
一:诊断
我也说了,“诊断”这是wcf的一个专业术语,意思也就是监控wcf的全部动向,若是往下说的话,能够分为监控 wcf的message 和 wcf
自己的服务状态信息和端对端的流转消息。
1. 端对端的流转消息
在玩wcf以前,不知道有多少人熟悉Diagnostics,对的,它就是.net自带的日志类,固然在这个年代,记录日志的组件有不少,好比
log4net,Nlog等等。。。不过话说回来,Diagnostics这个叼毛用起来还比较另类,它由“跟踪源” 和 “监听器”组成。分别就是TraceSource
来指定跟踪源,用TraceListener来指定跟踪源的监听器,因此理所固然,TraceSource的全部踪影都会被TraceListener监听到,下面咱们
看看怎么玩。
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.diagnostics> <sources> <source name="System.ServiceModel" switchValue="ActivityTracing"> <listeners> <add name="mylisteners" type="System.Diagnostics.XmlWriterTraceListener" initializeData="E:\1.txt" /> </listeners> </source> </sources> <trace autoflush="true"/> </system.diagnostics> <system.serviceModel> <behaviors> <serviceBehaviors> <behavior> <serviceMetadata httpGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="false" /> </behavior> </serviceBehaviors> </behaviors> <services> <service name="MyService.HomeService"> <endpoint address="HomeService" binding="wsHttpBinding" contract="MyService.IHomeService"> <identity> <dns value="localhost" /> </identity> </endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> <host> <baseAddresses> <add baseAddress="http://192.168.1.107:1920" /> </baseAddresses> </host> </service> </services> </system.serviceModel> </configuration>
从上面的配置中能够看到,你有没有发现我在配置system.diagnostics的时候和wcf一点关系都没有,我并无在system.ServiceModel
下对diagnostics有一丁点的配置,对吧,这说明什么,说明“踪影跟踪”功能和wcf一点关系都没有,但却能够完整的记录wcf的踪影信息,然
后我稍微解释下listeners节点,在这里我配置了一个XmlWriterTraceListener的监听器,而后把输出文件的路径配置在initializeData属性下,
其实都是diagnostics自己的知识范畴,和wcf一点关系都没有,好了,下面我开启下程序,看看到底都追踪到什么?
有没有看到,当个人服务启动以后,追踪信息就所有来了。。。可是接下来有一个问题来了,这个很杂乱的xml该怎么看才能最舒舒服服的
呢???不用着急啦,wcf一样给咱们提供了一个叫作SvcTraceView的工具,专门就是用来查找这个“踪影信息”的,工具的路径在:
C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools
下面的事情就是打开它,附加一下1.txt文件就行了,以下图:
从左边的“活动图”中大概能够看到HomeService这个服务启动到运行经历了一些什么样的悲惨故事。。。有兴趣的话,你们能够本身动
手试试啦。
2. 监控input和ouput的message
若是要监控message的话,咱们须要再定义一个TraceSource 和 TraceListener便可,不过此次监听的是System.ServiceModel.
MessageLogging跟踪源,而后在System.ServiceModel下面配置一下message的参数,以下:
1 <?xml version="1.0" encoding="utf-8"?> 2 <configuration> 3 4 <system.diagnostics> 5 <sources> 6 <source name="System.ServiceModel" switchValue="ActivityTracing"> 7 <listeners> 8 <add name="mylisteners" type="System.Diagnostics.XmlWriterTraceListener" initializeData="E:\1.txt" /> 9 </listeners> 10 </source> 11 <source name="System.ServiceModel.MessageLogging" switchValue="ActivityTracing"> 12 <listeners> 13 <add name="messagelogging" type="System.Diagnostics.XmlWriterTraceListener" initializeData="E:\2.txt"/> 14 </listeners> 15 </source> 16 </sources> 17 <trace autoflush="true"/> 18 </system.diagnostics> 19 20 <system.serviceModel> 21 22 <diagnostics> 23 <messageLogging logEntireMessage="true" logMalformedMessages="true" logMessagesAtTransportLevel="true" /> 24 </diagnostics> 25 26 <behaviors> 27 <serviceBehaviors> 28 <behavior> 29 <serviceMetadata httpGetEnabled="true" /> 30 <serviceDebug includeExceptionDetailInFaults="false" /> 31 </behavior> 32 </serviceBehaviors> 33 </behaviors> 34 35 <services> 36 <service name="MyService.HomeService"> 37 <endpoint address="HomeService" binding="basicHttpBinding" 38 contract="MyService.IHomeService"> 39 <identity> 40 <dns value="localhost" /> 41 </identity> 42 </endpoint> 43 <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> 44 <host> 45 <baseAddresses> 46 <add baseAddress="http://192.168.1.107:1920" /> 47 </baseAddresses> 48 </host> 49 </service> 50 </services> 51 52 </system.serviceModel> 53 54 </configuration>
此次我准备来跑一下客户端,调用Server端的Update方法,看看能抓到啥样的Messsage。
如今我火烧眉毛的想用SvcTraceView打开下2.txt,看看都拿到了什么追踪信息。。。
好了,这篇我也只是引路式的介绍下SvcTraceView,具体更深刻的玩法,你们能够琢磨琢磨,对了,若是你们想对Source和Listener的
一些参数须要进一步了解,能够参考下SvcConfigEditor,好比下面这样,一目了然,你懂的。。。
十五天精通WCF——第十二天 说说wcf中的那几种序列化
咱们都知道wcf是由信道栈组成的,在咱们传输的参数走到传输信道层以前,先须要通过序列化的过程,也就是将参数序列化为message,这篇
咱们就来讲说这里的序列化,蛮有意思的,可能初学者也明白,在wcf中默认的序列化是DataContractSerializer,确实是这样,不过wcf在信道中
其实不只仅支持DataContractSerializer,它还支持其余类型的序列化,好比XmlSerializer,NetDataContractSerializer以及DataContractJson
Serializer,下面咱们一块儿来见证下。
1. XmlSerializer
要了解XmlSerializer,咱们先来简单看看NetDataContractSerializer,在前面的文章中,我也说过DataContract就是将咱们的model序列化为
XSD,第二点就是使用DataContract的原则就是你必须在Model上加DataContract,并且在你要序列化的字段上加DataMember。这样才可以正确的序列
化,为了演示,咱们先看看默认的序列化Model会变成啥样?
1 [DataContract] 2 public class Student 3 { 4 [DataMember] 5 public int ID { get; set; } 6 7 [DataMember] 8 public string Name { get; set; } 9 10 [DataMember] 11 public string SNS { get; set; } 12 }
可是在有些状况下,你可能并不适合用DataContract,好比Model是第三方提供的,那么这个时候你的Model可能就不会有DataContract标记,那这样的
话wcf就没法进行序列化,那我若是非要保证wcf能正常跑起来的话,还有其余好的办法吗???固然了,确定有办法,这就比如谈恋爱同样,总不能
在一棵树上吊死吧,没人谁离不开谁,也不会谁离开了谁会死,天涯何处无芳草,男儿何患无妻,对吧。Wcf中也同样,既然DataContract用不了,自
然会有替代它的人,那这我的就是XmlSerializer,使用起来也很简单,就是在契约方法上面加上XmlSerializerFormat便可,而后咱们把Model的
DataContract所有去掉。
是否是很简单,下面咱们就要验证一下,看看这个Format是否进入到了这个Operation的Behavior中,
从上面的图中,你也看到了, XmlSerializerFormat 已经被注入到Behavior中,而且是由类XmlSerializerOperationBehavior代为处理。
接下来,咱们用fiddler监视一下,看看Message中的Body是否真的按照XmlSerializer 序列化了。
有没有看到,此次Message的Body已经和文章开头处的Message不同了。
2. NetDataContract
这个玩意也没什么好说的,光从表面上看,它和DataContract惟一不一样的地方就是多了一个Net,因此你大概也能猜到,这个功能大概和DataCont
ract同样,只不过比DataContract多了一个程序集保存,那这句话是什么意思呢???就是NetDataContract会把程序集的命名空间和类名都保存到XSD中,
在反序列化的过程当中必需要用一样的程序集才能解开,其实无论咱们是作SOA或者面向对象编程都讲究接口编程,而NetDataContract给你的印象就是面
向对象编程,固然这也有好处,好比说若是把程序集带进去就好像秘钥同样,必须有它才能解开,对吧,因此致使wcf项目组并不对NetDataContract感冒
,因此在实际应用上也不建议使用。
3. DataContractJsonSerializer
看到上面这个带有Json的字样,我想你们都知道这玩意是干什么的???没错,他就是将咱们的Model序列化成Json,这在wcf的rest编码使用的很广,
若是你们有兴趣的话,我在下一篇会详细描述,这里咱们先简单看一看。
好了,这一篇就说这些了,洗洗睡了。。。
十五天精通WCF——第十三天 用WCF来玩Rest
在咱们玩wcf的时候,都会潜意识的以为wcf就是经过soap协议交换消息的,而且能够在basic,tcp,msmq等等绑定中任意切换,
牛逼的一塌糊涂,可是呢,若是说哪一天wcf再也不使用soap协议,而是采用json格式的字符串,是否是有一点颠覆你对wcf的认识的???
从传统意义上说,wcf是很是重量级的,很明白的一个例子就是太多太多的配置,尤为是Behavior的配置,并且behavior对wcf来讲又是重
中之重,它对wcf的扩展和性能又是最重要的,可恨的是wcf在binding,behavior,contract之中的配置又是很是很是的保守,能够说用
wcf来玩分布式,这些默认配置是彻底作不到的,就好比说basicbinding的基类HttpBindingBase。
抱怨的话我也不说了,可能微软也以为这个问题是个不小的问题,而后就有了轻量级的 asp.net web api,你能够看到它和wcf比起来精
简多了,也许让咱们这些码农更加的专一于业务吧,既然wcf带了这玩意,我也得必须约谈一下。
一:UriTemplate
要说rest,还得先说UriTemplate,由于wcf用UriTemplate来作rest中的uri模板匹配,而后用WebInvoke这个OperationBehavior
插入到wcf的心脏中,说的玄乎一点,这个就有点像mvc中的路由匹配机制,下面我举个例子:
1. 用UriTemplate来告知能够监视的完整Url
从下面的图中,能够看到三个元素:服务地址,模板,入参(这里面的”1“),这三个元素组合在一块儿,就构成了完整的remote url,
而后这个完整的url就是我模板(/User/{id})监视的对象。
2. 经过UriTemplate来解析url中的参数。
既然能够构建url,那固然能够解析url啦,对吧,下面这张图能够很清晰的告知你,当外来的url=http://127.0.1:1920/HomeService
/User/1过来的时候应该被哪一个uriTemplate所接收。
正是由于UriTemplate具备这样的url构建和解析能力,因此wcf就把UriTemplate做为WebInvoke和WebGet这两个属性的参数来动态
解析外来的url,而后根据这个url分配到具体的服务方法上,下面咱们具体看一看。
二:WebGet,WebInvoke的使用
刚才也说了,WebGet和WebInvoke正是用了UriTemplate,才具备了路由转向的功能,还有就是默认返回的是xml,这里就用json
值做为服务返回的格式
1 [ServiceContract] 2 public interface IHomeService 3 { 4 [OperationContract] 5 [WebGet(UriTemplate = "Get/{id}", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] 6 Student Get(string id); 7 8 [OperationContract] 9 [WebInvoke(Method = "POST", UriTemplate = "Add", RequestFormat = WebMessageFormat.Json, 10 ResponseFormat = WebMessageFormat.Json)] 11 string Add(Student stu); 12 }
对了,Rest推荐使用Http协议中的Get,Post,Delete,Put来做为CURD的状态机制,而后就是你若是看懂了UriTemplate,那你如今应
该知道这个Template在监视什么类型的url。作完了上面的coding,下面咱们须要在webconfig中经过behavior来指定启动“web编程模型”,
就好比下面这样。
1 <?xml version="1.0" encoding="utf-8"?> 2 <configuration> 3 4 <system.diagnostics> 5 <sources> 6 <source name="System.ServiceModel" switchValue="ActivityTracing"> 7 <listeners> 8 <add name="mylisteners" type="System.Diagnostics.XmlWriterTraceListener" initializeData="E:\1.txt" /> 9 </listeners> 10 </source> 11 <source name="System.ServiceModel.MessageLogging" switchValue="ActivityTracing"> 12 <listeners> 13 <add name="messagelogging" type="System.Diagnostics.XmlWriterTraceListener" initializeData="E:\2.txt"/> 14 </listeners> 15 </source> 16 </sources> 17 <trace autoflush="true"/> 18 </system.diagnostics> 19 20 <system.serviceModel> 21 22 <diagnostics> 23 <messageLogging logEntireMessage="true" logMalformedMessages="true" logMessagesAtTransportLevel="true" /> 24 </diagnostics> 25 26 <behaviors> 27 <serviceBehaviors> 28 <behavior> 29 <serviceMetadata httpGetEnabled="true" /> 30 <serviceDebug includeExceptionDetailInFaults="true" /> 31 </behavior> 32 </serviceBehaviors> 33 <endpointBehaviors> 34 <behavior name="webbehavior"> 35 <webHttp /> 36 </behavior> 37 </endpointBehaviors> 38 </behaviors> 39 40 <services> 41 <service name="MyService.HomeService"> 42 <endpoint address="HomeService" binding="webHttpBinding" behaviorConfiguration="webbehavior" 43 contract="MyService.IHomeService"> 44 <identity> 45 <dns value="localhost" /> 46 </identity> 47 </endpoint> 48 <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> 49 <host> 50 <baseAddresses> 51 <add baseAddress="http://127.0.0.1:1920" /> 52 </baseAddresses> 53 </host> 54 </service> 55 </services> 56 57 </system.serviceModel> 58 59 </configuration>
其实呢?也就是代码中的WebHttpBehavior类
好了,我如今服务地址也出来了:http://127.0.0.1:1920 ,而后服务方法的template也指定了。只要http.sys监控到了template
匹配的url,服务方法就会被执行,好比我如今在浏览器里面输入:http://127.0.0.1:1920/HomeService/Get/1 来测试下Get操做。
能够看到,get方法成功了,也正确的匹配了个人服务方法Get。
1 public class HomeService : IHomeService 2 { 3 public Student Get(string id) 4 { 5 return new Student() { ID = Convert.ToInt32(id), Name = "hxc", SNS = "001" }; 6 } 7 8 public string Add(Student stu) 9 { 10 return "hello"; 11 } 12 }
而后咱们看看Add方法,我在HttpWebRequest中模拟测试以下。

好了,大概就说这么多了,若是说你不嫌麻烦,你能够用WCF Rest,还有就是不要忘了不少的默认配置,若是你以为太繁琐,
能够用用asp.net web api。
十五天精通WCF——第十四天 一块儿聊聊FaultException
咱们在玩web编程的时候,可能你会不经意的见到一些http500的错误,我想你应该不会陌生的,缘由你应该也知道,服务器异常嘛,
这时候clr会把这个未处理的异常抛给iis而且包装成http500的错误返回到客户端,就好比下面这样。
从这张图中,我故意输入了xss字符,而后的而后,web程序自爆异常,其实我想表达的意思就是,虽说web程序抛异常了,但不表明iis就
挂了,因此iis仍是须要给客户端作出反馈,这就有了http header,和body信息,一样的道理,wcf的服务器异常机制也是这样。。。service
抛出了异常,不表明console就挂了,console要作的事情就是把这个异常包装起来丢给调用方,而wcf是怎么包装的呢???就是用了这篇所
说的FaultException。。。
一:FaultException
1. faultexception是干什么的?
刚才我也说了,这个异常就是wcf来包装远程错误的,具体的类含义就是表示“SOAP错误“,若是你够细心的话,你还会发现到它有个属性
叫Serializable,有了它,这个叼毛就能够序列化到Soap消息中,对伐???
2. 若是挖出faultexception?
挖出这个exception的方法有不少,好比我来造一个“除以0”的异常,以下所示:
Service:
1 public class HomeService : IHomeService 2 { 3 public Student Get(string id) 4 { 5 //这里必然会抛出异常。。。 6 var result = Convert.ToInt32(id) / Convert.ToInt32("0"); 7 8 return new Student() { ID = Convert.ToInt32(id), Name = "hxc", SNS = "001" }; 9 } 10 }
Client:
1 public class Program1 2 { 3 static void Main(string[] args) 4 { 5 using (HomeServiceClient client = new HomeServiceClient()) 6 { 7 try 8 { 9 var result = client.Get("1"); 10 } 11 catch (Exception ex) 12 { 13 14 } 15 } 16 } 17 }
看到了没有,虽然wcf的service已经抛出异常了,可是仍是被clr用Faultexception包装起来了,正如你看到了s:Fault节点,仔细往下看的话,
你还会看到faultcode,faultstring,detail等等属性节点,那下面有个问题就来了,咱们平时在Client端都习惯这么写。
1 using (HomeServiceClient client = new HomeServiceClient()) 2 { 3 try 4 { 5 var result = client.Get("1"); 6 } 7 catch (Exception ex) 8 { 9 client.Abort(); 10 } 11 }
可是这么写有个什么问题呢???就是无论客户端抛出什么异常,咱们都习惯用基类异常Exception捕获,可是wcf有一点很是恶心的就是,
它的异常信息很是的少,第一眼根本看不出个一二三,这是由于全部的异常你都用顶级的exception捕获,天然你能知道的信息就很是少,
这也很正常,若是你想要更详细的信息,你是否是应该在Client端写上更具体的异常捕获类呢???就好比你如今已经知道的FaultException
是由于服务器的错误都是由它处理的。
若是如今你按照上图中所coding的那样,你是否是对异常信息能够了解的更深,起码你知道这个异常的抛出,绝逼是由于通道是正常的,只是
servcie抛出异常了而已。。。那你可能要问了,我这话的言外之意就是还有其余异常类也会捕获wcf抛出的异常,对的,好比说你的信道出现
故障,这时候会抛出一个“通讯异常(CommunicationException)”。
三:如何挖出“通讯异常”
挖出这个异常,也是很简单的,如今咱们须要使用”会话级别“的binding,好比说nettcpbinding,wshttpbinding,这里的话,我选择
后者,由于是这样的,第一次服务器抛异常之后,客户端和服务器端通讯信道就会关闭,若是你在客户端不从新new一个client,那么这时候你
第二次再使用client的话,这个时候就会产生“信道故障“,抛出CommunicationException,而当你看到CommunicationException的时候,
你能够很是有自信的说,老子的wcf根本就没有链接到service,而是在client端就被杀死了。。。下面我演示一下。
四:自定义FaultException
如今你应该知道了,只要是Servcie的Exception都会抛出 FaultException,对吧,并且你用Fiddler观察的话,也看的出其中的faultcode
和faultstring貌似都不是很详细,那我就有一个想法了,既然wcf会本身给我包装个FaultException,那何不我本身就在发生异常的时候本身包
装一个自定义的FaultException,而后我能够包装一些我本身想要告诉客户端的信息,这样的话是否是灵活性很是的大呢???想法很不错,wcf
也是恩准这么作的,下面我把service的get方法更改以下,在FaultException中自定义Reason,Code,Action等等自定义信息。
1 public class HomeService : IHomeService 2 { 3 public Student Get(string id) 4 { 5 try 6 { 7 //这里必然会抛出异常。。。 8 var result = Convert.ToInt32(id) / Convert.ToInt32("0"); 9 10 return new Student() { ID = Convert.ToInt32(id), Name = "hxc", SNS = "001" }; 11 } 12 catch (Exception ex) 13 { 14 var reason = new FaultReason("你这个战斗力只有五的渣渣。。。 这么简单的错误都出来了,搞个鸡巴毛"); 15 16 var code = new FaultCode("500"); 17 18 var faultException = new FaultException(reason, code, "是Get这个王八蛋"); 19 20 throw faultException; 21 } 22 } 23 }
好了,大概就说这么多了,个人目的也很简单,在写wcf的client的时候,尽可能作到异常越具体越好,这样方便咱们尽量快的排查问题,由于
wcf的异常信息真的太tmd坑爹了!!!减轻痛苦,从小作起~~~
十五天精通WCF——终结篇 那些你须要注意的坑
终于一路走来,到了本系列的最后一篇了,这一篇也没什么好说的,总体知识框架已经在前面的系列文章中讲完了,wcf的配置众多,若是
不加一些指定配置,你可能会遇到一些灾难性的后果,快来一睹为快吧。
一: 第一个大坑 【数据传输量】
咱们使用wcf的目的,就是用来进行分布式的数据交互,既然是交互,就必定要进行数据交换,可能一些新人并无注意到wcf在数据传输量上
面作了一个大小限制,好比我如今要传输一个2m的txt给service,会出现什么状况???
1 static void Main(string[] args) 2 { 3 try 4 { 5 var txt = File.ReadAllText("E:\\1.txt"); 6 7 HomeServiceClient client = new HomeServiceClient(); 8 9 client.Get(txt); 10 11 int i = 10; 12 13 } 14 catch (Exception ex) 15 { 16 17 throw; 18 } 19 }
但是的但是,咱们在玩aspnet的时候,再大的传输量都见过,但为何这玩意就抛异常了呢???下面一个问题就来了,这个传输默认值到底
是多少??? 接下来咱们就用ILSpy翻翻看。
能够看到,这个叼毛玩意竟然只有 64k。。。没错,你看到的就是64k,也就说明你的传输量不能大于64k,不然请求就会在client端拒绝,
知道了缘由,咱们如今就能够这么修改config了。
<bindings> <netTcpBinding> <binding name="MySessionBinding" maxReceivedMessageSize="2147483647"/> </netTcpBinding> </bindings>
有不少资料在配置这个坑的时候,也会使用MaxBufferSize 和 MaxBufferPoolSize,就是用来增长缓冲区和缓冲池的大小。
一: 第二个大坑 【并发量过低】
提及这个大坑,还得先从一段代码提及,下面是一段对服务进行2w次并发调用,而后咱们看看效果。
public class Program1 { static void Main(string[] args) { try { for (int i = 0; i < 200000; i++) { try { Task.Factory.StartNew((obj) => { try { HomeServiceClient client = new HomeServiceClient(); Console.WriteLine("第 {0} 个请求开始。。。", obj); client.Get("12312"); Console.WriteLine("第 {0} 个请求结束。。。", obj); } catch (Exception ex) { Console.WriteLine(ex.Message); } }, i); } catch (Exception ex) { Console.WriteLine(ex.Message); } } Console.Read(); } catch (Exception ex) { throw; } } }
从上面你能够看到,当并发数达到800左右的时候,servcie端就开始拒绝client端过来的请求了,而且以后的1min的时间里,client端
开始出现超时异常,这确定不是我想看到的, 那有人就要说了,个人并发达到800多很正常啊,若是提升这个并发呢???其实在wcf里面
有一个叫作ServiceThrottlingElement绑定元素,它就是用来控制服务端的并发数。
这三个属性的大概意思,我想你们都看的明白,不过有点奇怪的是,这三个属性的默认值 和 ILSpy中看到的不同。。。
也懒的研究源码了,无论怎么样,反正这三个属性值都是int类型的,因此我将他们设置为int.maxValue就行了。
<system.serviceModel> <behaviors > <serviceBehaviors > <behavior name="nettcpBehavior"> <serviceMetadata httpGetEnabled="false" /> <!--是否在错误中包含有关异常的详细信息--> <serviceDebug includeExceptionDetailInFaults="True" /> <serviceThrottling maxConcurrentCalls="2147483647" maxConcurrentInstances="2147483647" maxConcurrentSessions="2147483647" /> </behavior> </serviceBehaviors> </behaviors> <bindings> <netTcpBinding> <binding name="MySessionBinding" /> </netTcpBinding> </bindings> <services> <service behaviorConfiguration="nettcpBehavior" name="MyService.HomeService"> <endpoint address="net.tcp://127.0.0.1:19200/HomeService" binding="netTcpBinding" bindingConfiguration="MySessionBinding" contract="MyService.IHomeService" /> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> <host> <baseAddresses> <add baseAddress="http://127.0.0.1:1920" /> </baseAddresses> </host> </service> </services> </system.serviceModel>
而后咱们再把程序跑起来看一看。。。
如今你能够发现并发早已突破800了,不过你要记住,若是并发数太多,容易形成系统资源耗尽,致使崩溃,这时候负载均衡就来
了,对吧,wcf须要修改的配置还有不少,正由于wcf框架庞大,不少默认配置不符合生产需求,因此你们在工做中须要注意,这个系列
就到此打住了,但愿对你有帮助。