1.SvcUtil.exe是MS推出的服务于WCF的一款非常重要的工具,搞清楚这个工具的功能,对于加深自己对WCF的了解将很有好处。
功能1:代码生成
通过访问一个正在运行的服务或者一个静态的元数据文件,生成服务契约和客户端的代码(如果服务端也是我们自己开发的,那只有客户端代码对我们有意义了)。最常用的方式就是我们在IE地址栏内输入一个服务的地址的时候IE给出的提示信息。于是我创建了一个简单得不能再简单的服务,ServiceContract里面只声明了一个方法 string TestWCF(),而服务的实现就是返回一个常量字符串。对于这么简单的服务,如果是我自己来写这个客户端,将非常简单,如代码段1:
public class TenderBidClient : ClientBase<ITestWCFService>, ITestWCFService
{
public String TestWCF()
{
return base.Channel.TestWCF();
}
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.MessageContractAttribute(WrapperName="TestWCF",
WrapperNamespace="http://tempuri.org/", IsWrapped=true)]
public partial class TestWCFRequest
{
public TestWCFRequest()
{
}
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.MessageContractAttribute(WrapperName="TestWCFResponse",
WrapperNamespace="http://tempuri.org/", IsWrapped=true)]
public partial class TestWCFResponse
{
[System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://tempuri.org/", Order=0)]
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
public string TestWCFResult;
public TestWCFResponse()
{
}
public TestWCFResponse(string TestWCFResult)
{
this.TestWCFResult = TestWCFResult;
}
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public partial class TenderTopicListClient : System.ServiceModel.ClientBase<ITenderTopicList>, ITenderTopicList
{
/*这里还有一些TenderTopicListClient的构造器,被我省略了*/
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
TestWCFResponse ITenderTopicList.TestWCF(TestWCFRequest request)
{
return base.Channel.TestWCF(request);
}
public string TestWCF()
{
TestWCFRequest inValue = new TestWCFRequest();
TestWCFResponse retVal = ((ITenderTopicList)(this)).TestWCF(inValue);
return retVal.TestWCFResult;
}
}
可以看到,对于每一个要调用服务的传入参数和返回值,scvUtil都生成了一个自定义的MessageContract来进行封装(如代码段2中的TestWCFRequest和TestWCFResponse)。由于自己对WCF的用法还比较浅,目前还体会不太清楚这样的好处,但直观上感觉至少对传入参数和返回值有了更强的控制。而我自己的代码,其实参数和返回值也是通过消息来发送和接收的,只是这种消息封装是在调用服务的时候由系统自动完成的,从我的静态代码中看不到而已。
功能2:导出(下载)元数据
用于从一个程序集中(或者是一个正在运行的服务)导出关于服务、契约和数据类型的元数据信息。于是我随便儿取出一个含有ServiceContract定义的程序集,发现SvcUtil为我生成了以下几个文件:
(1)tempuri.org.xsd
这个schema定义了所有关于WCF服务的调用参数和返回值的结构。比如我前面提到过的string TestWCF()方法,对应的配置定义为代码段3:
<xs:element name="TestWCF">
<xs:complexType>
<xs:sequence />
</xs:complexType>
</xs:element>
<xs:element name="TestWCFResponse">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="TestWCFResult" nillable="true" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
对于做过Web Service开发的tx,这个文件肯定非常熟悉(俺没做过),如代码段4
<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tns="http://tempuri.org/" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract"
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
xmlns:wsa10="http://www.w3.org/2005/08/addressing"
xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex"
targetNamespace="http://tempuri.org/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
<wsdl:types>
<xsd:schema targetNamespace="http://tempuri.org/Imports">
<xsd:import namespace="http://tempuri.org/" />
<xsd:import namespace="http://schemas.microsoft.com/2003/10/Serialization/" />
<xsd:import />
</xsd:schema>
</wsdl:types>
<wsdl:message name="ITenderTopicList_TestWCF_InputMessage">
<wsdl:part name="parameters" element="tns:TestWCF" />
</wsdl:message>
<wsdl:message name="ITenderTopicList_TestWCF_OutputMessage">
<!--这里的element定义就取自代码段3-->
<wsdl:part name="parameters" element="tns:TestWCFResponse" />
</wsdl:message>
<wsdl:portType name="ITenderTopicList">
<wsdl:operation name="TestWCF">
<wsdl:input wsaw:Action="http://tempuri.org/ITenderTopicList/TestWCF"
message="tns:ITenderTopicList_TestWCF_InputMessage" />
<wsdl:output wsaw:Action="http://tempuri.org/ITenderTopicList/TestWCFResponse"
message="tns:ITenderTopicList_TestWCF_OutputMessage" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="DefaultBinding_ITenderTopicList" type="tns:ITenderTopicList">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="TestWCF">
<soap:operation soapAction="http://tempuri.org/ITenderTopicList/TestWCF" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
</wsdl:definitions>
这两个文件并不一定会出现在SvcUtil产生的元数据信息中,我这里之所以出现这两个文件,是因为在我定义的服务中,有一个Operation的返回值是DataSet,而DataSet是一个复杂类型,这个类型的序列化过程也许要一些元数据的定义。代码段5给出了这两个文件的部分内容:
<!--noNamespace.xsd的内容-->
<?xml version="1.0" encoding="utf-8"?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="DataSet" nillable="true">
<xs:complexType>
<xs:annotation>
<xs:appinfo>
<ActualType Name="DataSet" Namespace="http://schemas.datacontract.org/2004/07/System.Data"
xmlns="http://schemas.microsoft.com/2003/10/Serialization/" />
</xs:appinfo>
</xs:annotation>
<xs:sequence>
<xs:element ref="xs:schema" />
<xs:any />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
<!--schemas.microsoft.com.2003.10.Serialization.xsd的内容-->
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:tns="http://schemas.microsoft.com/2003/10/Serialization/" attributeFormDefault="qualified"
elementFormDefault="qualified" targetNamespace="http://schemas.microsoft.com/2003/10/Serialization/"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="anyType" nillable="true" type="xs:anyType" />
<xs:element name="anyURI" nillable="true" type="xs:anyURI" />
<xs:element name="base64Binary" nillable="true" type="xs:base64Binary" />
<xs:element name="boolean" nillable="true" type="xs:boolean" />
<xs:element name="byte" nillable="true" type="xs:byte" />
<xs:element name="dateTime" nillable="true" type="xs:dateTime" />
<xs:element name="decimal" nillable="true" type="xs:decimal" />
<xs:element name="double" nillable="true" type="xs:double" />
<xs:element name="float" nillable="true" type="xs:float" />
<xs:element name="int" nillable="true" type="xs:int" />
<xs:element name="long" nillable="true" type="xs:long" />
<xs:element name="QName" nillable="true" type="xs:QName" />
<xs:element name="short" nillable="true" type="xs:short" />
<xs:element name="string" nillable="true" type="xs:string" />
<xs:element name="unsignedByte" nillable="true" type="xs:unsignedByte" />
<xs:element name="unsignedInt" nillable="true" type="xs:unsignedInt" />
<xs:element name="unsignedLong" nillable="true" type="xs:unsignedLong" />
<xs:element name="unsignedShort" nillable="true" type="xs:unsignedShort" />
<xs:element name="char" nillable="true" type="tns:char" />
<xs:simpleType name="char">
<xs:restriction base="xs:int" />
</xs:simpleType>
<xs:element name="duration" nillable="true" type="tns:duration" />
<xs:simpleType name="duration">
<xs:restriction base="xs:duration">
<xs:pattern value="\-?P(\d*D)?(T(\d*H)?(\d*M)?(\d*(\.\d*)?S)?)?" />
<xs:minInclusive value="-P10675199DT2H48M5.4775808S" />
<xs:maxInclusive value="P10675199DT2H48M5.4775807S" />
</xs:restriction>
</xs:simpleType>
<xs:element name="guid" nillable="true" type="tns:guid" />
<xs:simpleType name="guid">
<xs:restriction base="xs:string">
<xs:pattern value="[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}" />
</xs:restriction>
</xs:simpleType>
<xs:attribute name="FactoryType" type="xs:QName" />
</xs:schema>
可以看到,在schemas.microsoft.com.2003.10.Serialization.xsd中定义了DataSet这个复杂类型内部可能用到的所有的类型的定义和序列化需要用到的其他类型相关信息(可否为空,采用正则的格式检查等)。
功能3:服务校验
也许当我们要使用一个外部服务的时候需要用到这个功能吧。
使用格式:
svcutil /validate /serviceName:myServiceName myServiceHost.exe
2.听了一个sohu工程师在csdn上的讲座,重点摘录如下:
(1)效率从来都不是卡在Web服务器上,而是DB服务器上
(2)作为网络应用开发人员,应该读一点http协议的内容(汗,从来没看过......)
(3)他谈到在Apache中间有一个module,名字也许叫Expires,说是很有用。因为我们的Web服务器很少有专门提供动态页面服务的,一般都会提供大量的静态数据(图片,flash等等),如果浏览器知道某个自己要请求的资源已经有了,并且未过期,它就不用去服务器取了,这就省下了不少带宽。不知道IIS在这方面又没有相关的配置,或者是在默认情况下已经进行了优化?自己回头要尝试一下。
(4)他谈到要关掉无用的log记录(在Apache中,默认情况下,所有的访问都计入log,也是挺耗资源的)
(5)提到了一个KeepAlive这样的开关,如果开关打开,则Web服务器会保持和客户端的连接状态,直到TimeOut。也就是说,在TimeOut之前,同一个客户端再次访问Web服务器是不需要建立连接的,这就提高了用户体验。但打开开关也比较耗资源,如果你的用户量上去了(按他说,每天超过120万),就会出现很多用户登不上系统的情况。不知道IIS里面有没有这类选项?
(6)关于MySql,他提到一些建议配置:连接数200(除非机器性能特别好,或者sql语句设计很好),数据表内行数小于800万行等。
(7)Order by和Group by都是非常耗时的操作,特别是和关联查询放在一起用,需要特别特别谨慎。他举例说,你把数据全部取出来,然后用Java这样的高级语言在内存里进行分组排序都要比数据库里面快。当然,这种结论的成立需要很多前提,但至少说明了这两种操作的资源占用程度。
(8)他提到,对数据库的效率而言,读和写永远是一对矛盾。想写得快,读起来肯定慢(索引少,表之间的关联少);反之亦然。
(9)squid也被多次提到。对普通的单位上网用户,Squid可充当代理服务器;而对Sina,NetEase这样的大型站点,Squid又充当WEB加速器。这两个角色它都扮演得异常优秀。他是开源世界的明星,不晓得在Windows体系下表现如何,有无用武之地。
(10)他提到可以单独建立图片服务器,这样一是增加图片命中率,一是减低Web服务器的网卡负担,一是可以和Web服务器采用不同的文件系统(比如Linux下的reserf文件系统,对大量小文件的目录,存取效率都很比ext3高,但安全性较差等等)。
(11)当访问上了量,网站提供的静态内容和动态内容一定要尽量分开,因为他们的优化策略和实现策略都是不同的。例如,他提到squid对静态文件很有意义,但对动态内容意义不大(想想也是,动态内容的缓存机制本来就比较难控制)。
今天听不完了,这方面的内容将独立记录在《听sohu工程师钱宏武先生的关于大型网站架构设计的讲座》。
今天的工作时间不长,有事儿先走了,下回继续 ^_^