1. 程式人生 > >ServiceFabric: 服務與服務之間是如何通訊的——DNS服務

ServiceFabric: 服務與服務之間是如何通訊的——DNS服務

目的

在平常做專案的時候,如果有兩個後端服務(比如兩個後端API服務),我們希望更輕鬆地在兩個服務之間建立連線,直接在同一個叢集內部進行互相訪問和通訊,而不希望它們之間通過暴露給外部的URL進行通訊(例如:服務B暴露給外部的URL是URL_B,如果服務A訪問服務B通過URL_B訪問的話,就繞彎路了)。本文將介紹ServiceFabric Cluster中的服務是如何在同一個叢集內部建立連線和通訊的。

服務發現和解析介紹

服務是有生命週期的,使用凡人皆有一死來描述服務很貼切,特別是在分散式系統中,服務可能隨時間推移從一臺計算機移動到另一臺計算機,這意味著服務終結點地址會在服務移動到具有不同 IP 地址的節點時發生更改,並且可能在不同埠上開啟(如果服務使用動態選擇的埠)。那這樣就引出了一個問題, 服務的IP地址不斷地在變,那我們如何使用這個服務呢?

命名服務

Service Fabric 提供一種服務發現和解析服務,稱為“命名服務”, 命名服務的名稱看起來像這樣:"fabric:/MyApplication/MyService",Service Fabric 具有一個註冊機構,它維護一個表,將服務名稱對映到其終結點地址,我們用得時候只需要使用命名服務的名稱就可以了,它所對應的服務的IP地址由Service Fabric 自行維護,這與Web 上的 DNS(將網站 URL 解析為 IP 地址)類似。

目前看起來只要使用了命名服務之後就已解決了伺服器之間的通訊問題,只需要知道命名服務的名稱就可找到服務的實際終結點(IP+Port)。但通常來說,我們希望通過URL(比如前端的angular程式碼或者其它容器化服務)來訪問此服務,而不是通過一個服務的名稱來訪問,所以我們需要一個DNS 服務。

DNS 服務

 DNS 服務能夠使用標準 DNS 協議將 DNS 名稱對映到服務名稱,命名服務將服務名稱進行解析並將其傳送回服務終結點。請求的圖看起來像下面這樣。

æå¡ç»ç»ç¹

 同一個叢集內的服務採用DNS通訊

首先Service Fabric cluster需要啟用DNS Service,如果你是使用port.azure.com或Visual Studio建立的叢集,那預設包含了DNS Service,如下圖。

 

 當然你也可以在當前的service fabric所在的資源組裡可以檢查或更新是否啟用 DNS 服務,如下圖

 在ServiceFabricDemo專案中,ApplicationManifest.xml中設定服務的 DNS 名稱,吐血推薦你使用 <ServiceDnsName>.<AppInstanceName>的命名規範,這裡使用ServiceDnsName="BackendContainerService.ServiceFabricDemo",如下是完整的

ApplicationManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<ApplicationManifest ApplicationTypeName="ServiceFabricDemoType"
                     ApplicationTypeVersion="1.0.0"
                     xmlns="http://schemas.microsoft.com/2011/01/fabric"
                     xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Parameters>
    <Parameter Name="FrontendContainerService_InstanceCount" DefaultValue="-1" />
    <Parameter Name="BackendContainerService_InstanceCount" DefaultValue="-1" />
    <Parameter Name="MyContainerService_InstanceCount" DefaultValue="-1" />
  </Parameters>
  <!-- Import the ServiceManifest from the ServicePackage. The ServiceManifestName and ServiceManifestVersion 
       should match the Name and Version attributes of the ServiceManifest element defined in the 
       ServiceManifest.xml file. -->
  <ServiceManifestImport>
    <ServiceManifestRef ServiceManifestName="FrontendContainerServicePkg" ServiceManifestVersion="1.0.0" />
    <ConfigOverrides />
    <Policies>
      <ContainerHostPolicies CodePackageRef="Code">
        <!-- See https://aka.ms/I7z0p9 for how to encrypt your repository password -->
        <RepositoryCredentials  AccountName="accenturecode" Password="Jb4tScXXXXXXXXXXXFKxwq76" PasswordEncrypted="false" />
        <PortBinding ContainerPort="80" EndpointRef="FrontendContainerServiceTypeEndpoint" />
      </ContainerHostPolicies>
    </Policies>
  </ServiceManifestImport>
  <ServiceManifestImport>
    <ServiceManifestRef ServiceManifestName="BackendContainerServicePkg" ServiceManifestVersion="1.0.0" />
    <ConfigOverrides />
    <Policies>
      <ContainerHostPolicies CodePackageRef="Code">
        <!-- See https://aka.ms/I7z0p9 for how to encrypt your repository password -->
        <RepositoryCredentials  AccountName="accenturecode" Password="Jb4tScXXXXXXXXXXXFKxwq76" PasswordEncrypted="false" />
        <PortBinding ContainerPort="80" EndpointRef="BackendContainerServiceTypeEndpoint" />
      </ContainerHostPolicies>
    </Policies>
  </ServiceManifestImport>
  <!--<ServiceManifestImport>
    <ServiceManifestRef ServiceManifestName="MyContainerServicePkg" ServiceManifestVersion="1.0.0" />
    <ConfigOverrides />
    <Policies>
      <ContainerHostPolicies CodePackageRef="Code">
        --><!-- See https://aka.ms/I7z0p9 for how to encrypt your repository password --><!--
        <RepositoryCredentials AccountName="" Password="" PasswordEncrypted="true" />
        <PortBinding ContainerPort="80" EndpointRef="MyContainerServiceTypeEndpoint" />
      </ContainerHostPolicies>
    </Policies>
  </ServiceManifestImport>-->
  <DefaultServices>
    <!-- The section below creates instances of service types, when an instance of this 
         application type is created. You can also create one or more instances of service type using the 
         ServiceFabric PowerShell module.
         
         The attribute ServiceTypeName below must match the name defined in the imported ServiceManifest.xml file. -->
    <Service Name="FrontendContainerService" ServicePackageActivationMode="ExclusiveProcess">
      <StatelessService ServiceTypeName="FrontendContainerServiceType" InstanceCount="[FrontendContainerService_InstanceCount]">
        <SingletonPartition />
      </StatelessService>
    </Service>
    <Service Name="BackendContainerService" ServicePackageActivationMode="ExclusiveProcess" ServiceDnsName="BackendContainerService.ServiceFabricDemo">
      <StatelessService ServiceTypeName="BackendContainerServiceType" InstanceCount="[BackendContainerService_InstanceCount]">
        <SingletonPartition />
      </StatelessService>
    </Service>
    <!--<Service Name="MyContainerService" ServicePackageActivationMode="ExclusiveProcess">
      <StatelessService ServiceTypeName="MyContainerServiceType" InstanceCount="[MyContainerService_InstanceCount]">
        <SingletonPartition />
      </StatelessService>
    </Service>-->
  </DefaultServices>
</ApplicationManifest>

右擊ServiceFabricDemo, 點選Publish,在彈出的框中直接點選Publish來重新部署,不超過10秒就部署好了,我們看一下Service Fabric Explorer, 可以看到Service的DNS Name.

 我們直接可以在Cluster內部使用此DNS Name進行訪問,我們來驗證一下。先找到Virtual machine scale set,它有一個公網IP,我們使用建立Cluster時輸入的使用者名稱和密碼登入到此虛擬機器中。

 

 輸入PowerShell命令,向http://backendcontainerservice.servicefabricdemo:83/api/Values發起請求

invoke-webrequest -uri http://backendcontainerservice.servicefabricdemo:83/api/Values

 可以看到使用此DNS Name在叢集內可以正常訪問,並返回了結果。

相關連結

總結與討論

本應該建立另一個後端的服務通過DNS服務來發送此請求的,但本文這裡只寫了一條PowerShell命令來驗證在叢集內部可以通過DNS Name進行訪問,這已經足夠了。

有一個疑問,可以在前端angular的程式碼中採用DNS通訊的方式來訪問後端嗎?不可以,因為當你訪問前端網站時,前端向後端發起的API請求實際上是你本機的瀏覽器向Service Fabric叢集裡的後端服務發起API請求,你本機的瀏覽器相對於Service Fabric叢集來說是外部請求,而DNS通訊的方式的用於服務之間的內部通訊。