2016-09-22 135 views
1

我將現有的Cloud Service WorkerRole移植到服務結構中作爲無狀態服務。原始的Cloud Service使用SignalR和Service Bus(作爲SignalR背板)將通知發送給任何聽取的客戶端。還有,做一個啓動類的一些設置的:在Azure服務結構服務中設置SignalR和服務總線

class Startup 
{ 
    public void Configuration(IAppBuilder app) 
    { 
     String connectionString = "Endpoint=sb://[name].servicebus.windows.net/;SharedSecretIssuer=owner;SharedSecretValue=[key]"; 
     GlobalHost.DependencyResolver.UseServiceBus(connectionString, "InSys"); 
     app.MapSignalR(); 
     Notifications.Hub = GlobalHost.ConnectionManager.GetHubContext<MyHub>(); 
    } 
} 

在的OnStart()方法中的WorkerRole我開球OWIN有:

var endpoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["HttpEndpoint"]; 
var baseUri = $"{endpoint.Protocol}://{endpoint.IPEndpoint}"; 
var app = WebApp.Start<Startup>(new StartOptions(url: baseUri)); 

這是怎麼回事(即連接SignalR Service Bus Backplane)完成服務結構中的無狀態服務?

回答

1

https://github.com/marcinbudny/SignalRSelfHostScaleOut(這是使用Redis進行擴展的示例)的幫助下,我想我有這個舔。

在ServiceManifest.xml添加以下端點:

<Endpoint Protocol="http" Name="ServiceEndpoint" Type="Input" Port="8322" /> 

我還添加了一個啓動類:

還加入
public static class Startup 
{ 
    public static void ConfigureApp(IAppBuilder app) 
    { 
     String connectionString = "Endpoint=sb://[name].servicebus.windows.net/;SharedSecretIssuer=owner;SharedSecretValue=[value]"; 
     GlobalHost.DependencyResolver.UseServiceBus(connectionString, "InSys"); 
     app.MapSignalR(); 
     Notifications.Hub = GlobalHost.ConnectionManager.GetHubContext<InSysMainHub>(); 
    } 
} 

一種OwinCommunicationListener類:

public class OwinCommunicationListener : ICommunicationListener 
{ 
    private readonly ServiceEventSource eventSource; 
    private readonly Action<IAppBuilder> startup; 
    private readonly ServiceContext serviceContext; 
    private readonly string endpointName; 
    private readonly string appRoot; 

    private IDisposable webApp; 
    private string publishAddress; 
    private string listeningAddress; 

    public OwinCommunicationListener(Action<IAppBuilder> startup, ServiceContext serviceContext, ServiceEventSource eventSource, string endpointName) 
     : this(startup, serviceContext, eventSource, endpointName, null) 
    { 
    } 

    public OwinCommunicationListener(Action<IAppBuilder> startup, ServiceContext serviceContext, ServiceEventSource eventSource, string endpointName, string appRoot) 
    { 
     if (startup == null) 
     { 
      throw new ArgumentNullException(nameof(startup)); 
     } 

     if (serviceContext == null) 
     { 
      throw new ArgumentNullException(nameof(serviceContext)); 
     } 

     if (endpointName == null) 
     { 
      throw new ArgumentNullException(nameof(endpointName)); 
     } 

     if (eventSource == null) 
     { 
      throw new ArgumentNullException(nameof(eventSource)); 
     } 

     this.startup = startup; 
     this.serviceContext = serviceContext; 
     this.endpointName = endpointName; 
     this.eventSource = eventSource; 
     this.appRoot = appRoot; 
    } 


    public Task<string> OpenAsync(CancellationToken cancellationToken) 
    { 
     var serviceEndpoint = this.serviceContext.CodePackageActivationContext.GetEndpoint(this.endpointName); 
     var protocol = serviceEndpoint.Protocol; 
     int port = serviceEndpoint.Port; 

     if (this.serviceContext is StatefulServiceContext) 
     { 
      StatefulServiceContext statefulServiceContext = (StatefulServiceContext) serviceContext; 

      listeningAddress = string.Format(
       CultureInfo.InvariantCulture, 
       "{0}://+:{1}/{2}{3}/{4}/{5}", 
       protocol, 
       port, 
       string.IsNullOrWhiteSpace(appRoot) 
        ? string.Empty 
        : appRoot.TrimEnd('/') + '/', 
       statefulServiceContext.PartitionId, 
       statefulServiceContext.ReplicaId, 
       Guid.NewGuid()); 
     } 
     else if (serviceContext is StatelessServiceContext) 
     { 
      listeningAddress = string.Format(
       CultureInfo.InvariantCulture, 
       "{0}://+:{1}/{2}", 
       protocol, 
       port, 
       string.IsNullOrWhiteSpace(appRoot) 
        ? string.Empty 
        : appRoot.TrimEnd('/') + '/'); 
     } 
     else 
     { 
      throw new InvalidOperationException(); 
     } 

     publishAddress = listeningAddress.Replace("+", FabricRuntime.GetNodeContext().IPAddressOrFQDN); 

     try 
     { 
      eventSource.Message("Starting web server on " + listeningAddress); 
      webApp = WebApp.Start(listeningAddress, appBuilder => startup.Invoke(appBuilder)); 
      eventSource.Message("Listening on " + this.publishAddress); 
      return Task.FromResult(this.publishAddress); 
     } 
     catch (Exception ex) 
     { 
      eventSource.Message("Web server failed to open endpoint {0}. {1}", this.endpointName, ex.ToString()); 
      StopWebServer(); 
      throw; 
     } 
    } 

    public Task CloseAsync(CancellationToken cancellationToken) 
    { 
     this.eventSource.Message("Closing web server on endpoint {0}", this.endpointName); 

     this.StopWebServer(); 

     return Task.FromResult(true); 
    } 

    public void Abort() 
    { 
     this.eventSource.Message("Aborting web server on endpoint {0}", this.endpointName); 

     this.StopWebServer(); 
    } 

    private void StopWebServer() 
    { 
     if (this.webApp != null) 
     { 
      try 
      { 
       this.webApp.Dispose(); 
      } 
      catch (ObjectDisposedException) 
      { 
       // no-op 
      } 
     } 
    } 
} 

最後我改變了我的無狀態服務器中的CreateServiceInstanceListeners方法副代碼爲:

protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners() 
    { 
     return new[] 
     { 
      new ServiceInstanceListener(serviceContext => new OwinCommunicationListener(Startup.ConfigureApp, serviceContext, ServiceEventSource.Current, "ServiceEndpoint")) 
     }; 
    } 
0

使用Owin偵聽器創建無狀態服務。然後在啓動時配置signalR和背板(服務總線或sql)。理想情況下,您將面臨的問題是協商(Signalr客戶端與服務器之間的握手)此時嘗試配置跨源請求,持久連接的示例代碼如下所示。

另請注意,行appBuilder.UseAesDataProtectorProvider(「Your Key」),因爲它很重要。這樣做的結果是,你不會最終獲得HTTP 400來連接大部分時間。這是因爲SignalR至少會在握手時發出兩個請求,而這些請求通常會碰到兩臺不同的機器。

感謝marcin budny的解釋。

var config = new HttpConfiguration(); 

// Configure your origins as required. 

var cors = new EnableCorsAttribute("*", "*", "*"); 

config.EnableCors(cors); 

FormatterConfig.ConfigureFormatters(config.Formatters); 

RouteConfig.RegisterRoutes(config.Routes); 

appBuilder.UseWebApi(config); 

GlobalHost.DependencyResolver.UseServiceBus("yourconnection string comes here", "signalrbackplaneserver"); 
appBuilder.UseAesDataProtectorProvider("some password"); 
appBuilder.Map("/echo", map => 
{ 
       map.UseCors(CorsOptions.AllowAll).RunSignalR<MyEndPoint>(); 
});