快速定义面向服务

简单地说,面向服务是一种分布式应用组件通过消息和契约实现松耦合的架构风格。面向服务的应用是通过契约描述它们交互中使用的消息。这些契约必须使用一种语言描述。并且它的格式能够被其他应用简单地理解,因此可以减少组件实现带来的依赖性。

理解消息

在面向服务的应用中,消息是通信的基本单位。因此,面向服务的应用通常被称为消息应用系统。在某一时刻,每个面向服务的应用系统都会发送或接收消息。同样,一个面向服务的消息可以被计算机发送,发送给另一个计算机,并且可能由另外的计算机来投递。这些与面向服务消息交互的实体称为消息参与者。

消息参与者

当描述消息参与者的时候,把它们对应到消息发送过程的角色是非常有用的。通常来说,有三种类型的消息参与者:初始发送者、最终接收者和中介者。

让我们考虑一个真实的商业场景——Contoso回飞棒公司的订单系统。基本上,客户在网上订购回飞棒,为了处理和完成订单流程,网站产生一个消息发送到其他的系统,如图1-1所示。

图1-1 Contoso回飞棒公司的消息流

Contoso订单处理系统至少有两个消息参与者:网站是消息发送者,内部系统是消息接收者。可能还有一个负载均衡路由负责转发消息到合适的系统。如图1-2所示,可以认为路由就是中介者。

图1-2 带消息路由的Contoso回飞棒公司的消息流

初始发送者

发送者是一个发起通信的实体。

订单处理系统乍一看,网站可能就是消息的最初发送者;可是从内部系统的角度看,也许不是这样。事实上消息的最初发送者是相对的。相对,是指最初的消息发送者可以随赋予消息的上下文环境改变而改变。在例子里,可以画出任意一个包围两个或多个消息参与者的边界,并且改变消息的最初发送者。

中介者

中介者对发送者是不可见的,并且处于发送者和接收者中间。

最终接收者

最终接收者是消息期望到达的目标

消息剖析

由于SOAP的灵活性,目前SO消息的消息都是SOAP消息。SOAP它的核心是一个基于XML的消息结构。SOAP定义了3个主要的可以定义任意XML消息的XML元素:信封、消息主体和消息头。以下是个简单的SOAP消息例子:

<?xml version='1.0' ?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
<env:Header>...</env:Header>
<env:Body>...</env:Body>
</env:Envelope>

消息信封

消息信封包含消息体和消息头。所有的SOAP消息都有一个消息信封作为根元素。

消息头

消息头是可以选择的,如果出现,就必须是消息信封encelope标签下的第一个元素。SOAP消息头由一个或多个消息头块组成。SOAP消息头块包含可以被最终接收者和中介者使用的信息。最典型的就是这些消息头块包含描述消息体数据的信息。换句话说,安全、关联或消息上下文信息都可以放到消息头部分里。如果需要特定的消息行为,消息头块是必须的。

消息体

消息体是必须的,它包含消息的有效数据。不能保证中介者不打开和改变SOAP消息体。使用数字签名和加密可能能保证消息从初始发送者到最终接收 者的完整性。

消息传输

SOAP消息是独立于传输的。换句话说,没必要在消息中添加任何传输规范。这个简单的特性是许多关键特性之一,它使得这个消息结构无比强大。

消息编码

某些情况下,基于文本的XML数据大小限制了它的使用,特别是当要通过网络发送一个XML消息的时候。

XML Infoset

XML规范要求定义格式良好,XML文档必须包含一个开始和结束元素、一个根节点等。奇怪的是,XML规范发布以后,激起了抽象定义XML文档的需求。XML Infoset(定义在http://www.w3.org/TR/xml-infoset/)提供了这个抽象定义。

实际上,XML Infoset定义是条目之间的关系,而不是定义任何具体的语法,这种设计为以后使用新的、更加高效的编码机制提供了扩展性。WCF提供了三种编码器:文本编码器、二进制编码器和MTOM(消息传输优化机制)编码器。

选择恰当的编码

消息编码器强迫人们去考虑当前和将来的消息使用问题。绝大多数情况下,应用互操作性和消息中的数据类型就决定了我们的选择。表1-1列举了消息编码、可以发送的消息类型与可以接收这种消息的系统之间的对应关系。

表1-1 消息编码器的适用场景
消息类型 Binary Text MTOM
Text内容,只与WCF交互 1 2 3
Text内容,与现代非WCF系统交互 N/A 1 2
Text内容,与旧的非WCFsystems交互 N/A 1 N/A
大量的二进制内容,只与WCF交互 1 3 2
大量的二进制内容,与现代非WCF系统交互 N/A 2 1
大量的二进制内容,与旧的非WCFsystems交互 N/A 1 N/A
少量的二进制内容,只与WCF交互 1 2 3
少量的二进制内容,与现代非WCF系统交互 N/A 1 2
少量的二进制内容,与旧的非WCFsystems交互 N/A 1 N/A

标记消息地址

<?xml version='1.0' ?>
<Envelope>
<Header>
    <!--识别消息-->
    <MessageIdentifier>1</MessageIdentifier>
    <!--指定最终接收者-->
    <SendTo>http://wintelloct.com/OrderService</SendTo>
    <!--指定操作-->
    <Op>http://wintelloct.com/OrderSerrvice/ArchiveMessage</Op>
    <!--谁在监听消息-->
    <Reply>http://someotherurl.com/OrderReplyService</Reply>
    <!--指定初始发送者-->
    <SendFrom>http://wintelloct.com/SendService</SendFrom>
    <!--指定错误发送者-->
    <OnError>http://wintelloct.com/ErrorService</OnError>
</Header>
<Body>...</Body>
</Envelope>

WS-Addressing

WS-Addressing规范定义了传输层中常见的两种结构。目的就是实现传输中立。

终结点引用

到目前为止,已经使用初始发送者、消息中介者和最终接收者去描述消息交换过程中不同的实体。简单来说,一个服务终结点就是一个消息发送的目标。正如WS-Addressing规范中定义的一样,服务终结点引用就是一种描述服务终结点的方式。

这些逻辑上的属性都已经在XML Infoset元素中定义了。一些属性,比如Reference Properties、Reference Parameters和Policy可以包含XML元素的信息。以下例子演示的就是在XML中如何表示这些属性。

<wsa:EndpointReference xmlns:wsa="http://schemas.xmlSOAP...">
<wsa:Address>...<wsa:Address>
<wsa:ReferenceProperties>...</wsa:ReferenceProperties>
<wsa:ReferenceParameters>...</wsa:ReferenceParameters>
<wsa:PortType>...</wsa:PortType>
<wsa:ServiceName>...</wsa:ServiceName>
<wsp:Policy>...</wsp:Policy>
</wsa:EndpointReference>

消息头块

WS-Addressing同样定义了一些标准的SOAP消息头块,可以用来标注消息地址。以下代码块包含一些消息信息头块和WS-Addressing规范定义的数据类型。

<wsa:MessageID>xs:anyURI</wsa:MessageID>
<wsa:RelatesTo RelationshipType="..."?>xs:anyURI<wsa:RelatesTo>
<wsa:To>xs:anyURI</wsa:To>
<wsa:Action>xs:anyURI</wsa:Action>
<wsa:From>endpoint-referrence</wsa:From>
<wsa:ReplyTo>endpoint-referrence</wsa:ReplyTo>
<wsa:FaultTo>endpoint-referrence</wsa:FaultTo>

消息头块依赖

特定消息头块的信息依赖于其他消息中消息头块的内容。例如,ReplyTo表示回发消息的MessageID,这就可以解释为什么必须有MessageID了,表1-2描述了这种标准想头块的依赖性。

表1-2 消息头块的依赖性
Header# Header Name Min Occurs Max Occurs Depends On
1 wsa:MessageID 0 1 N/A
2 wsa:RelatesTo 0 Unbounded N/A
3 wsa:ReolyTo 0 1 1
4 wsa:From 0 1 N/A
5 wsa:FaultTo 0 1 1
6 wsa:To 1 1 N/A
7 wsa:Action 1 1 N/A

面向服务的4个原则

边界清晰

在面向服务里,服务可以通过消息与每个其他的服务交互。服务可以穿越边界发送消息给其他服务。服务可以发送和接收消息,也能被发送和接收的消息形状定义服务边界。这些边界被良好的定义,清晰地表示,并且是唯一的服务功能访问点。

服务自治

服务是可以控制生命周期的,能控制可用性和另外服务的边界。

契约共享

因为面向服务关注参与者之间传递的消息,所以必须有一个方式可以描述这些消息以及成功的消息交换需要什么条件。从广义上讲,这些描述称为契约。面向服务的系统用XSD和WSDL来表述契约。更确切点说,Schema用来描述消息结构,WSDL用来描述消息终结点。这些基于XML的契约表示发送和接收的消息结构、终结点地址、网络协议、安全需求等。本质上,一个消息发送者需要依赖于契约而不是服务本身。

基于策略的兼容性

服务必须能够描述其他服务与之交互的底层环境。

概念汇总

接下来看看这些概念是如何在WCF系统里工作的。在我们的例子里,将构建一个简单的接收客户订单的订单处理服务。

很显然,在面向服务系统开发首先应该创建契约。为了让例子简单,一个订单包含三个字段:产品ID、数量和状态消息。订单处理服务里,消息发送者和接收者统一使用WS-Addressing规范的SOAP消息来限制消息的结构。

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
<s:Header><wsa:Action s:mustUnderstand="1">urn:SubmitOrder</wsa:Action><wsa:MessageID>4</MessageID><wsa:ReplyTo><wsa:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:Address></wsa:ReplyTo><wsa:To s:mustUnderstand="1">http://localhost:8000/Order</wsa:To>
</s:Header>
<s:Body><Order><ProdID>6</ProdID><Qty>6</Qty><Status>order placed</Status></Order>
</s:Body>
</s:Envelope>

其实,我们无须亲自来做这些工作。基本上,C#描述的契约都可以根据需要转化为基于XSD和WSDL的契约。当使用C#表示契约时,可以选择定义一个类或接口。

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;[ServiceContract(Namespace ="http://wintellec.com/ProcessOrder")]
public interface IProcessOrder
{[OperationContract(Action ="urn:SubmitOrder")]void SubmitOrder(Message order);
}

现在有了自己的契约,就开始建立一个接收者程序。第一个商业订单一个构建一个实现定义好的契约接口。

/// <summary>
/// 实现程序集定义的接口
/// </summary>
public sealed class MyService : IProcessOrder
{public void SubmitOrder(Message order){//根据MessageID创建文件名string fileName = "Order" + order.Headers.MessageId.ToString() + ".xml";//提示消息到达Console.WriteLine("Message ID {0} received", order.Headers.MessageId.ToString());//创建一个XmlDictionaryWriter区写文件XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(new FileStream(fileName, FileMode.Create));//写消息到文件里order.WriteMessage(writer);writer.Close();}
}

下一个任务就是让MyService类型去接收内部消息。

    static void ServiceOpen(){//定义服务绑定WSHttpBinding binding = new WSHttpBinding(SecurityMode.None);//使用文本编码binding.MessageEncoding = WSMessageEncoding.Text;//定义服务地址Uri addressURI = new Uri("http://localhost:4000/Order");//使用MyService实例化服务宿舍ServiceHost svc = new ServiceHost(typeof(MyService));//给服务增加一个终结点svc.AddServiceEndpoint(typeof(IProcessOrder), binding, addressURI);//打开服务宿主开始侦听svc.Open();Console.WriteLine("The receiver is ready");Console.ReadLine();}

下面就是消息发送应用程序

 static void Main(string[] args){//开启监听服务ServiceOpen();//接收程序的地址EndpointAddress address = new EndpointAddress("http://localhost:4000/Order");//定义一个通信的服务//在这个例子里,可以使用WS的HTTP绑定WSHttpBinding binding = new WSHttpBinding(SecurityMode.None);binding.MessageEncoding = WSMessageEncoding.Text;//创建通道ChannelFactory<IProcessOrder> channel = new ChannelFactory<IProcessOrder>(binding, address);//使用通道工厂创建代理IProcessOrder proxy = channel.CreateChannel();//创建一些消息Message msg = null;for(int i=0;i<10;i++){//调用方法创建消息//注意使用在IProcessOrder契约里定义的Actionmsg= GenerateMessage(i, i);//SOAP消息头里添加MessageIDUniqueId uniqueId = new UniqueId(i.ToString());msg.Headers.MessageId = uniqueId;Console.WriteLine("Sending Message # {0}",uniqueId.ToString());//SOAP消息头里添加Actionmsg.Headers.Action = "urn:SubmitOrder";//发送消息proxy.SubmitOrder(msg);}}/// <summary>/// 创建消息的方法/// </summary>/// <param name="productID"></param>/// <param name="qty"></param>/// <returns></returns>private static Message GenerateMessage(int productID,int qty){MemoryStream stream = new MemoryStream();XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(stream, Encoding.UTF8, false);writer.WriteStartElement("Order");writer.WriteElementString("ProdID", productID.ToString());writer.WriteElementString("Qty", qty.ToString());writer.WriteEndElement();writer.Flush();stream.Position = 0;XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(stream, XmlDictionaryReaderQuotas.Max);return Message.CreateMessage(MessageVersion.Soap12WSAddressing10, string.Empty, reader);}

运行程序,检查服务写过的文件,会看到如下代码:

<?xml version="1.0" encoding="utf-8"?><s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing"><s:Header><a:Action s:mustUnderstand="1">urn:SubmitOrder</a:Action><a:MessageID>0</a:MessageID><a:ReplyTo><a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address></a:ReplyTo><a:To s:mustUnderstand="1">http://localhost:4000/Order</a:To></s:Header><s:Body><Order><ProdID>0</ProdID><Qty>0</Qty></Order></s:Body>
</s:Envelope>

消息头应该和我们在WS-Addressing规范里看到的一样,有些奇怪,并且它们的值应该与在消息发送应用里设置的属性一样。事实上,System.ServiceModel.Message类型展示出了一个叫做Headers属性,这个属性可以表示WS-Addressing的消息头。

为什么要面向服务?

回答很简单:可伸缩性、维护性、互操作性和灵活性。过去,分布式组件技术如COM紧紧地把所有的组件绑定在一起。最低限度上,这些分布式技术必须分享公共类型系统,并且通常是一个运行时。有这些依赖,软件升级变得十分复杂、费时费力。面向服务的应用系统恰恰相反,不需要依赖相同类型,就会展示出更适合企业计算需求的行为特征。

版本升级

应用系统需求会随着时间的变化而变化。在面向服务的应用系统里,消息发送者和接收者之间唯一协定就是契约。只要保持契约不变,两者都可以根据自己的期望自由改变自己的实现。

负载均衡

每个应用都有一些瓶颈,而且这些瓶颈有时候阻止一个应用系统为了增加吞吐量而进行的扩展工作。面向服务的应用程序可以更容易扩展应用系统需要,降低了总成本,而且简化了配置管理工作。

扩展一个面向组件的应用系统

扩展一个面向服务的应用系统

平台一直在变

随着时间的推移,平台变化非常快。面向服务使平台使用了独立的XML语法来表示消息契约,这个契约把发送者和接收者解耦。发送者接收遵守契约产生和发送消息,而接收者接收接收和处理消息。不需要序列化平台规范到消息里,所以终结点可以不受关注的平台版本更新影响。

基于内容的路由

路由的信息可以放置到消息头里,终结点可以根据信息头来判断消息路径。

端到端的安全

使用标准的XML安全机制通过面向服务的消息来提供端到端的安全。即使消息被持久到日志文件或破解攻击,如果消息使用标准的XML安全机制加密,消息里的数据仍然是可以保证安全的。

互操作性

面向服务的应用系统是与平台无关的。与平台无关是XML语法表述消息契约的普遍本性。发送消息给一个终结点而不知道它所运行的平台,这完全是可能的。

WCF技术内幕之面向服务相关推荐

  1. 《WCF技术内幕》翻译2:《WCF技术内幕》绪论

    绪论 总述     服务是现代软件架构的一个主要部分,WCF是构建基于Microsoft Windows系统的服务程序平台.WCF编写的服务可以与其它供应商的服务交互(例如, IBM, BEA, an ...

  2. 《WCF技术内幕》翻译1:《WCF技术内幕》目录和作者简介

    翻译序言: 我现在推荐一本很好的WCF学习书籍:<Inside Microsoft Windows Communication Foundation>.Justin Smith先生所著.2 ...

  3. 《WCF技术内幕》翻译5:第1部分_第1章_蓝月亮:WCF介绍和本章小结

    WCF介绍   在上世纪90年代微软和其他公司看到了互联的普遍需求和面向服务的普遍概念.那时,还没有被普遍接受的消息标准,结果,就没有平台.应用程序编程接口API.或者能够让开发者轻易创建面向服务的应 ...

  4. 经典技术书籍分享(5):《WCF技术内幕》封面和购买地址

    经过很长时间的等待,<WCF技术内幕>这本书最终确定确定封面,会于下周三,也就是2010年7月21日印刷完毕. 定价为39.8RMB~~~现在China Pub打折后29块多. 网上已经可 ...

  5. 《WCF技术内幕》翻译15:第1部分_第3章_消息交换模式、拓扑与编排:消息拓扑、消息编排和本章小结...

    消息拓扑 消息拓扑描述的是在一个或多个发送者和接受者之间消息如何发送的.消息拓扑可以描述简单的应用-应用的连接关系,但是它同样可以描述复杂的应用-企业的连接.在后续文章里,面向服务的应用的作用会显现出 ...

  6. 《WCF技术内幕》翻译32:第2部分_第6章_通道:通道状态机(1)

    通道状态机 通道和通道工厂拥有相同的特性,这些特性独立于运行时功能.其中最重要的特性之一就是他们拥有公共的状态机.WCF程序里的每个通道和通道工厂都有一个预定义的状态集合和一个预定义的方法集合,这些方 ...

  7. 《WCF技术内幕》翻译31:第2部分_第6章_通道:概述与正确认识通道。

    Chapter 6: Channels 第6章:通道(Channel) Overview 概述 Channels in Perspective 正确认识Channel The Channel Stat ...

  8. 《WCF技术内幕》翻译25:第2部分_第5章_消息:创建一个消息(下)之MessageFault

    Message和SOAP Fault老徐备注1 Message类型定义了一些用来创建表示SOAP Fault消息对象的工厂方法.SOAP Fault是SOAP消息的一种形式,它用来表示错误信息.在SO ...

  9. AJAX应用服务器端:面向服务与WCF

    WCF作为AJAX服务平台的优势                 WCF为启用AJAX的Web服务提供管道形式服务,并未服务器与客户端之间的通信提供了统一的编程模式.WCF运行环境非常灵活,它包含了能 ...

  10. 软考系统架构设计师范文2:论面向服务的架构及其应用

    论面向服务的架构(设计)及其应用 摘要: 2017年5月,我参加了公司"数据中心管理系统"项目的开发,并担任系统架构师职务,负责系统的架构设计.该系统旨在将公司分散在全国各地的数据 ...

最新文章

  1. boost::type_erasure::less_than_comparable相关的测试程序
  2. 我的模块加载系统 v17的入门教程2
  3. Javascript的面对对象的理解 【上】(prototype,函数和对象等概念)
  4. 数据结构--队列、双端队列实际举例详解(Python代码)
  5. HIVE--数据倾斜解决办法
  6. [emuch.net]MatrixComputations(7-12)
  7. 如何写毕业论文的参考文献?
  8. 记录有关步态识别的一些内容
  9. Linux 文件删除不了? 一招教你搞定!
  10. 如何将svg格式图片转换为其他格式图片,如png
  11. JLink 警告:The connected J-Link is defective,Proper operation cannot be guaranteed.所遇到的坑
  12. KITTI 自动驾驶 数据集 镜像 百度网盘 百度云 下载
  13. idea中js函数中使用EL表达式报错expression expected以及参数underfined的问题.md
  14. 分享23种追女生的方式,教你同时把追MM和设计模式融汇贯通(上
  15. 简单的学生在线练习系统(PHP版)
  16. springboot+shiro+redis+jwt实现多端登录:PC端和移动端同时在线(不同终端可同时在线)
  17. Hyper-v创建虚拟交换机与主机通信 ping通
  18. 2022年四川省工业设计中心认定奖补名单及申报奖补条件、程序及管理
  19. Rest架构风格详解
  20. 在所有的N位数中,有多少个数中有偶数个数字3(说明,0是偶数)?

热门文章

  1. rotate list java_Rotate List | Java最短代码实现
  2. HDFS简介及其功能
  3. Codeforces Round #446 (Div. 2) D. Gluttony 构造,思维
  4. 浅谈:字符串、时间格式的转换
  5. MongoDB 学习笔记(一):安装及简单shell操作
  6. 杭电 2838 牛叉的树状数组
  7. 【原】对txt文本进行数据读取
  8. 向数据库中的字段添加空值
  9. 为什么梯度反方向是函数下降最快的方向?
  10. nvidia显卡驱动,cuda,和cudnn版本