使用VPN或者ER服务建立云服务和本地服务网络通道来搭建混合应用的方式,需要网络设备的配合和比较复杂的网络配置,所以不是特别的方便。如果是不希望对本地网络环境做修改,而只是服务层面的混合,那么可以使用一个更方便的服务 – Azure中继服务。基于Azure中继服务访问本地服务,客户端不需要与本地服务建立直接连接,也不需要了解服务所在的位置,并且本地服务无需在防火墙上打开任何入站端口。

Azure中继服务支持传统的单向消息传送、请求/响应消息传送和对等消息传送。它还支持 Internet 范围的事件分发,以实现发布-订阅方案和双向套接字通信,从而提高点到点通信效率。在中继消息传送模式中,本地服务会通过出站端口连接至中继服务,并为绑定至特定会合地址的通信创建一个双向套接字。然后,客户端可以通过将消息发送到抵达会合地址的中继服务来与本地服务通信。

目前Azure提供两种中继服务,WCF(Windows Communication Foundation)中继和混合连接。其中WCF中继是传统的服务,适合于基于WCF的本地服务。(关于WCF请参阅 WCF 官方文档)。而混合连接使用开放标准Web套接字,可实现多平台方案,适用性更广。

本文介绍如何使用WCF中继来构建混合应用。

WCF中继

WCF中继允许你向公有云公开位于企业网络内的WCF服务,而无需打开防火墙连接,也无需对企业网络基础结构进行彻底的更改。而且它还允许你安全的控制谁可以访问这些服务。另外对你原来的WCF项目所需要做的改动也是很小的。

基于TCP的SOAP访问

适用于支持SOAP协议的客户端。

创建WCF中继服务

可以登陆Azure中国门户网站,按照界面提示一步步创建。也可以通过PowerShell命令,如下,其中创建wcf中继时,WcfRelayType设置为NetTcp。RequiresTransportSecurity和RequiresClientAuthorization默认都是true,这里显示指定只是处于演示作用。

  1. # login to Azure China with your account
  2. Login-AzureRmAccount -Environment AzureChinaCloud
  3.  
  4. $rgName = "relaydemorg"
  5. $namespaceName = "relaydemons"
  6. $location = "China East"
  7. $relayType = "NetTcp"
  8. $relayName = "tcpdata"
  9.  
  10. New-AzureRmResourceGroup -Name $rgName -Location $location
  11. New-AzureRmRelayNamespace -ResourceGroupName $rgName -Name $namespaceName -Location $location
  12. New-AzureRmWcfRelay -ResourceGroupName $rgName -Namespace $namespaceName -Name $relayName -WcfRelayType $relayType -RequiresTransportSecurity $true -RequiresClientAuthorization $true

创建出来的WCF中继服务地址是sb://relaydemons.servicebus.chinacloudapi.cn/tcpdata

通过WCF中继公开本地WCF服务

给本地服务安装服务总线NuGet包,默认安装最新版本。

  1. PM> Install-Package WindowsAzure.ServiceBus

安装完成后,在config文件里面会自动添加一些扩展的行为和绑定声明,比如:

  1. <behaviorExtensions>
  2. <add name="transportClientEndpointBehavior"
  3. type="Microsoft.ServiceBus.Configuration.TransportClientEndpointBehaviorElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
  4. </behaviorExtensions>
  5. <bindingExtensions>
  6. <add name="netTcpRelayBinding"
  7. type="Microsoft.ServiceBus.Configuration.NetTcpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
  8. </bindingExtensions>

View

文件配置方式

修改config文件,添加如下endpointBehavior,其中key需要有监听权限

  1. <endpointBehaviors>
  2. <behavior name="sbTokenProvider">
  3. <transportClientEndpointBehavior>
  4. <tokenProvider>
  5. <sharedAccessSignature keyName="<listenKey>" key="<keyValue>" />
  6. </tokenProvider>
  7. </transportClientEndpointBehavior>
  8. </behavior>
  9. </endpointBehaviors>

View

前面创建的WCF中继服务类型时NetTcp,所以这里添加绑定为netTcpRelayBinding的终结点,并指向前面创建的WCF中继服务地址

  1. <endpoint contract="<yourcontract>"
  2. binding="netTcpRelayBinding"
  3. address="sb://relaydemons.servicebus.chinacloudapi.cn/tcpdata"
  4. behaviorConfiguration="sbTokenProvider"/>

View

代码方式

注意,这里中继服务地址不能用ServiceBusEnvironment.CreateServiceUri("sb", "namespace", "tcpdata"))来生成,因为它生成的地址不是中国Azure的。

  1. string relaySvcAddress = "sb://relaydemons.servicebus.chinacloudapi.cn/tcpdata";
  2. ServiceHost sh = new ServiceHost(typeof(<yourservice>));
  3. sh.AddServiceEndpoint(typeof(<yourservice>), new NetTcpRelayBinding(), relaySvcAddress)
  4. .EndpointBehaviors.Add(new TransportClientEndpointBehavior
  5. {
  6. TokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider("listenKey", "keyValue")
  7. });

因为WCF支持对同一个服务使用多个网络终结点,所以可以在添加中继服务终结点以便外部访问的同时保留现有的内部终结点。

创建客户端进行SOAP访问

客户端也需要安装服务总线NuGet包,相应的中继服务相关扩展也会自动添加在Config文件中。

文件配置方式,跟服务端一样,也需要添加同样的endpointBehavior,只是key换成需要发送权限。另外添加跟服务端完全一致的终结点配置。

  1. <endpointBehaviors>
  2. <behavior name="sbTokenProvider">
  3. <transportClientEndpointBehavior>
  4. <tokenProvider>
  5. <sharedAccessSignature keyName="<sendKey>" key="<keyValue>" />
  6. </tokenProvider>
  7. </transportClientEndpointBehavior>
  8. </behavior>
  9. </endpointBehaviors>

View

代码方式也是跟服务端类似,添加相应的NetTcpBinding,终结点地址和TransportClientEndpointBehavior。

  1. string relaySvcAddress = "sb://relaydemons.servicebus.chinacloudapi.cn/tcpdata";
  2. var cf = new ChannelFactory<yourcontract>(
  3. new NetTcpRelayBinding(),
  4. new EndpointAddress(relaySvcAddress));
  5.  
  6. cf.Endpoint.Behaviors.Add(new TransportClientEndpointBehavior
  7. {
  8. TokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider("<sendKey>","<keyValue>")
  9. });

基于HTTP的REST访问

WCF服务本身支持服务以REST的形式公开出来,那么同样的,客户端也希望能用普通的HTTP来访问。对于这种形式,WCF中继服务也是支持的。

创建WCF中继服务

跟前面TCP方式的步骤一样,唯一的区别是WcfRelayType设置为Http,服务名称也做相应的命名,比如httpdata。这样创建出来的WCF中继服务地址是https://relaydemons.servicebus.chinacloudapi.cn/httpdata

通过WCF中继公开本地WCF服务

与TCP方式对比,有两个不一样的地方

  1. Host使用WebServiceHost
  2. 绑定使用WebHttpRelayBinding

通过HTTP方式公开,默认情况下是可以允许任何客户端访问的,这样不安全,所以需要在WebHttpRelayBinding里配置RelayClientAuthenticationType为RelayAccessToken,这样客户端就必须提供正确令牌才能访问。

文件配置方式

  1. <system.serviceModel>
  2. <bindings>
  3. <webHttpRelayBinding>
  4. <binding name="default">
  5. <security relayClientAuthenticationType="RelayAccessToken"/>
  6. </binding>
  7. </webHttpRelayBinding>
  8. </bindings>
  9. <behaviors>
  10. <endpointBehaviors>
  11. <behavior name="sbTokenProvider">
  12. <transportClientEndpointBehavior>
  13. <tokenProvider>
  14. <sharedAccessSignature keyName="listenKey" key="keyValue" />
  15. </tokenProvider>
  16. </transportClientEndpointBehavior>
  17. </behavior>
  18. </endpointBehaviors>
  19. </behaviors>
  20. <services>
  21. <service name="yourservice">
  22. <endpoint name="sbRelayEndpoint"
  23. address="https://relaydemons.servicebus.chinacloudapi.cn/httpdata"
  24. binding="webHttpRelayBinding"
  25. bindingConfiguration="default"
  26. behaviorConfiguration="sbTokenProvider"
  27. contract="yourcontract" />
  28. </service>
  29. </services>
  30. <extensions>
  31. <!—Install Microsoft Service Bus NuGet package will automatically add needed all extensions -->
  32. ……
  33. </extensions>
  34. </system.serviceModel>

View

代码方式

  1. string relaySvcAddress = "https://relaydemons.servicebus.chinacloudapi.cn/httpdata";
  2. WebServiceHost wsh = new WebServiceHost(typeof(yourservice));
  3. wsh.AddServiceEndpoint(
  4. typeof(yourservice),
  5. new WebHttpRelayBinding(
  6. EndToEndWebHttpSecurityMode.Transport,
  7. RelayClientAuthenticationType.RelayAccessToken),
  8. relaySvcAddress)
  9. .EndpointBehaviors.Add(new TransportClientEndpointBehavior
  10. {
  11. TokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider("listeningKey", "keyValue")
  12. });

客户端

因为前面服务端配置为安全访问方式,所以客户端需要提供正确令牌。那么如何生成令牌呢?有两种方式,一是利用服务总线包里的内置方法,二是自己生成,前者增加了对服务总线包的依赖性,但简单方便,后者需要对服务总线令牌的要求有一定的了解。以下提供两种方式的代码示例。

利用服务总线包的内置方法

  1. var token = TokenProvider.CreateSharedAccessSignatureTokenProvider("keyname", "key").GetWebTokenAsync(resourceUri, string.Empty, true, TimeSpan.FromDays()).Result;

自己生成

  1. private string createToken(string resourceUri, string keyName, string key)
  2. {
  3. TimeSpan sinceEpoch = DateTime.UtcNow - new DateTime(, , );
  4. var week = * * * ;
  5. var expiry = Convert.ToString((int)sinceEpoch.TotalSeconds + week);
  6. string stringToSign = HttpUtility.UrlEncode(resourceUri) + "\n" + expiry;
  7. HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key));
  8. var signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
  9. var sasToken = String.Format(CultureInfo.InvariantCulture,
  10. "SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}",
  11. HttpUtility.UrlEncode(resourceUri),
  12. HttpUtility.UrlEncode(signature),
  13. expiry,
  14. keyName);
  15. return sasToken;
  16. }

示例代码中的resourceUri就是前面创建的中继服务地址:https://relaydemons.servicebus.chinacloudapi.cn/httpdata

有了令牌后,就可以使用Http客户端来调用了,比如使用HttpClient。

  1. string relaySvcAddress = "https://relaydemons.servicebus.chinacloudapi.cn/httpdata";
  2. using (var httpClient = new HttpClient())
  3. {
  4. httpClient.DefaultRequestHeaders.Add("Authorization", token);
  5. var requestUri = $"{relaySvcAddress}/yourapimethod";
  6. using (var response = httpClient.GetAsync(requestUri).Result)
  7. {
  8. // handle response
  9. }
  10. }

提示:直接把服务总线的Key提供在客户端是不安全的,可以另外提供一个专门生成token的服务,这样客户端就可以需要的时候就从那个服务获取token,而不是直接拿到Key自己生成。

使用IIS托管服务

当本地服务是用IIS的方式来托管时,有一个特别需要注意的地方,IIS托管的服务只有在接受到请求后才会启动,所以如果一开始直接调用通过中继公开出来的服务是会失败的,因为本地托管在IIS里的服务还没启动,也就还没与中继服务建立链接,因此需要一些额外的配置来预热。那么该如何配置来预热呢?

可以按照以下步骤通过IIS的Application Initialization功能来做到:

确保Application Initialization功能已经安装。(角色 => Web Server (IIS) => Web Server => Application Development)

设置web应用对应的ApplicationPool启动模式为AlwaysRunning

设置web应用Preload Enabled为True

添加如下初始页配置到web应用的web.config中,一般初始页设置为访问wcf svc页面即可。

  1. <system.webServer>
  2. <applicationInitialization>
  3. <add initializationPage="/WebDataService.svc"/>
  4. </applicationInitialization>
  5. </system.webServer>

View

这样的话,就相当于web应用部署好之后会马上访问配置好的初始页来进行预热,从而与Service bus relay建立好链接,客户端就可以访问了。如果IIS重启也会执行预热操作以保证服务的运行。

*完整示例代码:查看

构建混合应用方式之WCF中继的更多相关文章

  1. 构建混合应用方式之 - Azure混合连接

    前面介绍了通过WCF中继构建混合应用的方式,由于对WCF的依赖,使得其使用有一定的局限性,基本上只适用于本地服务是WCF的.NET应用.而混合连接则弥补了这一块的缺陷,除了支持原有WCF中继的功能之外 ...

  2. 完全使用接口方式调用WCF 服务

    客户端调用WCF服务可以通过添加服务引用的方式添加,这种方式使用起来比较简单,适合小项目使用.服务端与服务端的耦合较深,而且添加服务引用的方式生成一大堆臃肿的文件.本例探讨一种使用接口的方式使用WCF ...

  3. WCF技术剖析之二十九:换种不同的方式调用WCF服务[提供源代码下载]

    原文:WCF技术剖析之二十九:换种不同的方式调用WCF服务[提供源代码下载] 我们有两种典型的WCF调用方式:通过SvcUtil.exe(或者添加Web引用)导入发布的服务元数据生成服务代理相关的代码 ...

  4. Post方式调用wcf服务

    我们平常在PC端调用WCF服务,只要知道WCF服务的地址,客户端直接添加引用服务就可以使用了,殊不知还有其他方式,其实,我们也可以 通过HTTP POST的方式调用WCF服务,这样就不用添加引用了,在 ...

  5. 通过代码的方式完成WCF服务的寄宿工作

    使用纯代码的方式进行服务寄宿 服务寄宿的目的是为了开启一个进程,为WCF服务提供一个运行的环境.通过为服务添加一个或者多个终结点,使之暴露给潜在的服务消费,服务消费者通过匹配的终结点对该服务进行调用, ...

  6. 用c/c++混合编程方式为ios/android实现一个自绘日期选择控件(一)

    本文为原创,如有转载,请注明出处:http://www.cnblogs.com/jackybu 前言 章节: 1.需求描述以及c/c++实现日期和月历的基本操作 2.ios实现自绘日期选择控件 3.a ...

  7. 如果选择构建ui界面方式,手写代码,xib和StoryBoard间的博弈

    代码手写UI这种方法经常被学院派的极客或者依赖多人合作的大型项目大规模使用. 大型多人合作项目使用代码构建UI,主要是看中纯代码在版本管理时的优势,检查追踪改动以及进行代码合并相对容易一些. 另外,代 ...

  8. 通过Web Deploy方式部署WCF

    如何发布WCF, 其实它有很多种方式去发布WCF服务到IIS上,这篇文章将介绍通过Web Deploy的发布方式去部署. 步骤: 在IIS上创建一个网站 打开IIS, 右击“Site” -> & ...

  9. 构建混合云:配置Azure site to site VPN连接(1)

      用户在构建自己云计算解决方案的时候,往往会选择私有云或者公有云来做部署,但在一些场景下,用户更加希望通过混合云的方案来满足自己的业务需求.Azure为混合云的部署提供多种不同的连接方案,最常见的是 ...

随机推荐

  1. conda 使用清华大学开源软件镜像

    conda 使用清华大学开源软件镜像 Anaconda的安装步骤不在本文的讨论中,我们主要是学习一下如何配置conda的镜像,以及一些问题的解决过程 配置镜像 在conda安装好之后,默认的镜像是官方 ...

  2. iOS开发常用第三方开源框架 持续更新中...

    键盘管理 TPKeyboardAvoiding IQKeyboardManager(1.2.8) 弹窗HUD MBProgressHUD(0.9.2) SVProgressHUD UIView+Toa ...

  3. Spring Security教程系列(一)基础篇-1

    第 1 章 一个简单的HelloWorld 第 1 章 一个简单的HelloWorld Spring Security中可以使用Acegi-1.x时代的普通配置方式,也可以使用从2.0时代才出现的命名 ...

  4. (HTTPS)-强制 SSL (HTTPS)Filter

    汗,无知真可怕,Servlert规范中已经有自动跳转到保护页面(Http - Https)的方法了: web.xml       <security-constraint>         ...

  5. 一些java考过的测试题和自己制作模拟服务端和客户端

    媒体 1,java环境变量: PATH: .;%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;  CLASSPATH: .;%JAVA_HOME%\jre\lib\rt.jar ...

  6. SQL Server AG集群启动不起来的临时自救大招

    SQL Server AG集群启动不起来的临时自救大招 背景 前晚一朋友遇到AG集群发生来回切换不稳定的情况,情急之下,朋友在命令行使用命令重启WSFC集群 结果重启WSFC集群之后,非但没有好转,导 ...

  7. VR全景,让VR不再是“空中楼阁“——智慧城市常诚

    VR的风口来了又走,而VR技术的支持者却始终在探索VR在各个领域的应用.最近,有业内专家表示,VR给带来的真正好处是,容易让人产生同理心,但同理心究竟能帮助我们做什么呢? 我第一次见到挪威建筑师Haa ...

  8. cpp(第十三章)

    1.动态(晚期)联编需要显示定义复制构造函数,赋值运算符,虚构函数. 2.纯虚类不能声明对象. 3.赋值运算符的特征标随类而异. 4.返回类型协变,重新定义继承的方法,应确保与原来的原型完全相同,但如 ...

  9. python基础操作_元组_字典操作

    #元组'''元组是不可变的列表,不能改.取值和列表一样'''tp=(1,2,3)tp1=('127.0.0.1','3307')#元组只有count 和index两个方法.lis=['127.0.0. ...

  10. HTML标签类型及特点

    关键词:块级元素  行级元素 行内块元素   一. 概述           HTML(Hyper Text Markup Language )作为一种标记语言,网页所有的内容均书写在标签内部,标签是 ...